pax_global_header00006660000000000000000000000064147051754600014523gustar00rootroot0000000000000052 comment=1c186338632100b93c36ae5c07672729d2f770c8 fenics-basix-0.9.0/000077500000000000000000000000001470517546000141045ustar00rootroot00000000000000fenics-basix-0.9.0/.clang-format000066400000000000000000000053651470517546000164700ustar00rootroot00000000000000--- Language: Cpp # BasedOnStyle: LLVM AccessModifierOffset: -2 AlignAfterOpenBracket: Align AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false AlignEscapedNewlinesLeft: false AlignOperands: true AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: All AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: true BinPackArguments: true BinPackParameters: true BraceWrapping: AfterClass: false AfterControlStatement: false AfterEnum: false AfterFunction: false AfterNamespace: false AfterObjCDeclaration: false AfterStruct: false AfterUnion: false BeforeCatch: false BeforeElse: false IndentBraces: false BreakBeforeBinaryOperators: All BreakBeforeBraces: Allman BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true ColumnLimit: 80 CommentPragmas: '^ IWYU pragma:' ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] IncludeCategories: - Regex: '^"(llvm|llvm-c|clang|clang-c)/' Priority: 2 - Regex: '^(<|"(gtest|isl|json)/)' Priority: 3 - Regex: '.*' Priority: 1 IncludeIsMainRegex: '$' IndentCaseLabels: false IndentWidth: 2 IndentWrappedFunctionNames: false JavaScriptQuotes: Leave JavaScriptWrapImports: true KeepEmptyLinesAtTheStartOfBlocks: true MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 1 NamespaceIndentation: None ObjCBlockIndentWidth: 2 ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: true PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 60 PointerAlignment: Left ReflowComments: true SortIncludes: true SpaceAfterCStyleCast: false SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Cpp11 TabWidth: 8 UseTab: Never ... fenics-basix-0.9.0/.github/000077500000000000000000000000001470517546000154445ustar00rootroot00000000000000fenics-basix-0.9.0/.github/FUNDING.yml000066400000000000000000000002141470517546000172560ustar00rootroot00000000000000# These are supported funding model platforms github: FEniCS # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] fenics-basix-0.9.0/.github/workflows/000077500000000000000000000000001470517546000175015ustar00rootroot00000000000000fenics-basix-0.9.0/.github/workflows/build-wheels.yml000066400000000000000000000064251470517546000226170ustar00rootroot00000000000000name: Build wheels # By default this action does not push to test or production PyPI. The wheels # are available as an artifact that can be downloaded and tested locally. on: workflow_dispatch: inputs: basix_ref: description: "Basix git ref to checkout" default: "main" type: string test_pypi_publish: description: "Publish to Test PyPi (true | false)" default: false type: boolean pypi_publish: description: "Publish to PyPi (true | false)" default: false type: boolean workflow_call: inputs: basix_ref: description: "Basix git ref to checkout" default: "main" type: string test_pypi_publish: description: "Publish to Test PyPi (true | false)" default: false type: boolean pypi_publish: description: "Publish to PyPi (true | false))" default: false type: boolean jobs: build_wheels: name: Build wheels on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-13, macos-14, windows-2022] steps: - uses: actions/checkout@v4 with: ref: ${{ github.event.inputs.basix_ref }} - name: Set up QEMU if: runner.os == 'Linux' uses: docker/setup-qemu-action@v2 with: platforms: arm64 - name: Export GitHub Actions cache environment variables if: runner.os == 'Windows' uses: actions/github-script@v6 with: script: | core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); - name: Build wheels uses: pypa/cibuildwheel@v2.21.1 - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: ${{ matrix.os }}-wheel-artifact path: ./wheelhouse/*.whl build_sdist: name: Build source distribution runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: ref: ${{ github.event.inputs.basix_ref }} - name: Upgrade pip run: python -m pip install --upgrade pip setuptools - name: Install build run: python -m pip install build - name: Build sdist run: python -m build --sdist . - name: Upload sdist uses: actions/upload-artifact@v4 with: name: src-artifact path: dist/* upload_pypi: name: Upload to PyPI (optional) needs: [build_wheels, build_sdist] runs-on: ubuntu-latest steps: - uses: actions/download-artifact@v4 with: pattern: '*-artifact' path: dist/ merge-multiple: true - uses: pypa/gh-action-pypi-publish@release/v1 if: ${{ github.event.inputs.pypi_publish == 'true' }} with: user: __token__ password: ${{ secrets.PYPI_TOKEN }} repository-url: https://upload.pypi.org/legacy/ - uses: pypa/gh-action-pypi-publish@release/v1 if: ${{ github.event.inputs.test_pypi_publish == 'true' }} with: user: __token__ password: ${{ secrets.PYPI_TEST_TOKEN }} repository-url: https://test.pypi.org/legacy/ fenics-basix-0.9.0/.github/workflows/dolfinx-tests.yml000066400000000000000000000071241470517546000230330ustar00rootroot00000000000000name: DOLFINx integration # This workflow will install Basix, FFCx, DOLFINx and run the DOLFINx # unit tests. on: pull_request: branches: - main merge_group: branches: - main workflow_dispatch: inputs: dolfinx_branch: description: "DOLFINx branch or tag" default: "main" type: string ffcx_branch: description: "FFCx branch or tag" default: "main" type: string ufl_branch: description: "UFL branch or tag" default: "main" type: string jobs: build: name: Run DOLFINx tests runs-on: ubuntu-latest container: ghcr.io/fenics/test-env:current-openmpi env: PETSC_ARCH: linux-gnu-complex64-32 OMPI_ALLOW_RUN_AS_ROOT: 1 OMPI_ALLOW_RUN_AS_ROOT_CONFIRM: 1 steps: - uses: actions/checkout@v4 - name: Install Basix run: | cmake -G Ninja -DCMAKE_BUILD_TYPE=Developer -B build-dir -S ./cpp cmake --build build-dir cmake --install build-dir python3 -m pip install --break-system-packages ./python - name: Install FEniCS Python components if: github.event_name != 'workflow_dispatch' run: | python3 -m pip install --break-system-packages git+https://github.com/FEniCS/ufl.git python3 -m pip install --break-system-packages git+https://github.com/FEniCS/ffcx.git - name: Install FEniCS Python components if: github.event_name == 'workflow_dispatch' run: | python3 -m pip install --break-system-packages git+https://github.com/FEniCS/ufl.git@${{ github.event.inputs.ufl_branch }} python3 -m pip install --break-system-packages git+https://github.com/FEniCS/ffcx.git@${{ github.event.inputs.ffcx_branch }} - name: Get DOLFINx if: github.event_name != 'workflow_dispatch' uses: actions/checkout@v4 with: path: ./dolfinx repository: FEniCS/dolfinx ref: main - name: Get DOLFINx if: github.event_name == 'workflow_dispatch' uses: actions/checkout@v4 with: path: ./dolfinx repository: FEniCS/dolfinx ref: ${{ github.event.inputs.dolfinx_branch }} - name: Install DOLFINx (C++) run: | cmake -G Ninja -DCMAKE_BUILD_TYPE=Developer -B build -S dolfinx/cpp/ cmake --build build cmake --install build - name: Install DOLFINx (Python) run: | python3 -m pip install --break-system-packages -r dolfinx/python/build-requirements.txt # TO REMOVE python3 -m pip -v install --break-system-packages --check-build-dependencies --no-build-isolation dolfinx/python/ - name: Run mypy checks run: | python3 -m pip install --break-system-packages mypy cd dolfinx/python python3 -m mypy dolfinx - name: Build DOLFINx C++ unit tests run: | cmake -G Ninja -DCMAKE_BUILD_TYPE=Developer -B build/test/ -S dolfinx/cpp/test/ cmake --build build/test - name: Run DOLFINx C++ unit tests run: | cd build/test ctest -V --output-on-failure -R unittests - name: Install Python demo/test dependencies run: python3 -m pip install --break-system-packages matplotlib numba pyamg pytest pytest-xdist scipy - name: Run DOLFINx Python unit tests run: | cd dolfinx python3 -m pytest -n auto python/test/unit - name: Run DOLFINx Python demos run: | cd dolfinx/ python3 -m pytest -n auto -m serial --durations=10 python/demo/test.py fenics-basix-0.9.0/.github/workflows/ffcx-tests.yml000066400000000000000000000035331470517546000223160ustar00rootroot00000000000000name: FFCx integration # Install Basix and FFCx and run the FFCx unit tests. on: pull_request: branches: - main push: branches: - "main" merge_group: branches: - main workflow_dispatch: inputs: ffcx_branch: description: "FFCx branch or tag" default: "main" type: string ufl_branch: description: "UFL branch or tag" default: "main" type: string jobs: build: name: Run FFCx tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.12' - name: Install dependencies run: sudo apt-get install -y libopenblas-dev liblapack-dev graphviz libgraphviz-dev ninja-build - name: Install UFL (default branch) if: github.event_name != 'workflow_dispatch' run: pip install git+https://github.com/FEniCS/ufl.git - name: Install UFL (specified branch) if: github.event_name == 'workflow_dispatch' run: pip install git+https://github.com/FEniCS/ufl.git@${{ github.event.inputs.ufl_branch }} - name: Install Basix run: pip -v install .[ci] - name: Get FFCx source (default branch) if: github.event_name != 'workflow_dispatch' uses: actions/checkout@v4 with: path: ./ffcx repository: FEniCS/ffcx ref: main - name: Get FFCx source (specified branch) if: github.event_name == 'workflow_dispatch' uses: actions/checkout@v4 with: path: ./ffcx repository: FEniCS/ffcx ref: ${{ github.event.inputs.ffcx_branch }} - name: Install FFCx run: pip install ./ffcx[ci] - name: Run FFCx tests run: pytest -n auto --ignore=ffcx/test/test_lnodes.py ffcx/test fenics-basix-0.9.0/.github/workflows/intel.yml000066400000000000000000000035011470517546000213360ustar00rootroot00000000000000name: oneAPI # Test Basix using Intel oneAPI compilers and MKL on: push: branches: - "main" pull_request: branches: - main merge_group: branches: - main workflow_dispatch: jobs: build: name: Build and test (oneAPI) runs-on: ubuntu-latest container: ubuntu:22.04 # Run in container to test with minimal pre-installed packages env: CC: icx CXX: icpx DEBIAN_FRONTEND: noninteractive steps: - name: Setup cmake uses: jwlawson/actions-setup-cmake@v2 with: cmake-version: '3.30.x' - name: Install required packages to install Intel compilers and to build run: apt -y update && apt install -y bzip2 git gnupg ninja-build make wget - uses: actions/checkout@v4 - name: Install Intel compilers and Intel Python run: | wget -O- https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB | gpg --dearmor | tee /usr/share/keyrings/oneapi-archive-keyring.gpg > /dev/null echo "deb [signed-by=/usr/share/keyrings/oneapi-archive-keyring.gpg] https://apt.repos.intel.com/oneapi all main" | tee /etc/apt/sources.list.d/oneAPI.list apt update apt install -y intel-oneapi-compiler-dpcpp-cpp intel-oneapi-mkl intel-oneapi-python libstdc++-11-dev - name: Install Basix run: | . /opt/intel/oneapi/setvars.sh pip -v install .[test] - name: Run units tests run: | . /opt/intel/oneapi/setvars.sh pip install pytest-xdist pytest -n auto --durations 20 test/ - name: Run Python demos run: | . /opt/intel/oneapi/setvars.sh pytest demo/python/test.py - name: Run C++ demos run: | . /opt/intel/oneapi/setvars.sh pytest demo/cpp/test.py fenics-basix-0.9.0/.github/workflows/mac.yml000066400000000000000000000020371470517546000207660ustar00rootroot00000000000000name: Basix CI on MacOS # This workflow will install Python dependencies, run tests and lint # with a single version of Python For more information see: # https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions on: pull_request: branches: - main push: tags: - "v*" branches: - main merge_group: branches: - main workflow_dispatch: jobs: build: name: Build and test (MacOS) runs-on: macos-13 steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.12" - name: Install dependencies (non-Python) run: brew install ninja - name: Install Basix run: pip -v install .[test] - name: Run units tests run: | pip install pytest-xdist pytest -n auto --durations 20 test/ - name: Run python demos run: pytest demo/python/test.py - name: Run C++ demos run: pytest demo/cpp/test.py fenics-basix-0.9.0/.github/workflows/pythonapp.yml000066400000000000000000000163501470517546000222530ustar00rootroot00000000000000name: Basix CI # This workflow will install Python dependencies, run tests and lint # with a single version of Python For more information see: # https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions on: push: branches: - "**" tags: - "v*" pull_request: branches: - main merge_group: branches: - main workflow_dispatch: jobs: lint: name: Lint code runs-on: ubuntu-latest steps: - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.12' - uses: actions/checkout@v4 - name: Ruff check run: | pip install ruff ruff check . ruff format --check . - name: MyPy check run: | pip install mypy numpy pip install git+https://github.com/FEniCS/ufl.git mypy python/basix build: name: Build and test runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-22.04] python-version: ["3.9", "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y doxygen graphviz libopenblas-dev liblapack-dev ninja-build - name: Install UFL branch run: pip -v install git+https://github.com/FEniCS/ufl.git - name: Install Basix run: pip -v install .[ci] - name: Run units tests run: pytest -n auto --durations 20 test/ - name: Run simple CMake integration test run: | cd test/test_cmake cmake -DCMAKE_BUILD_TYPE=Debug -DPython3_EXECUTABLE=python3 -G Ninja -B build-dir -S . cmake --build build-dir/ build-dir/a.out - name: Run Python demos run: pytest demo/python/test.py - name: Run C++ demos run: pytest demo/cpp/test.py - name: Build documentation run: | export BASIX_VERSION=`python3 -c "import basix; print(basix.__version__)"` cd doc/cpp doxygen cd ../python python -m sphinx -W -b html source build/html rm -rf build/html/.doctrees - name: Upload C++ documentation artifact uses: actions/upload-artifact@v4 with: name: doc-cpp-${{ matrix.python-version }} path: | doc/cpp/html retention-days: 2 if-no-files-found: error - name: Upload Python documentation artifact uses: actions/upload-artifact@v4 with: name: doc-python-${{ matrix.python-version }} path: | doc/python/build/html retention-days: 2 if-no-files-found: error - name: Build website documentation run: | sudo apt-get install -y ruby-bundler ruby-dev export BASIX_VERSION=`python3 -c "import basix; print(basix.__version__)"` cd doc/web python make_html.py - name: Set version name if: ${{ github.repository == 'FEniCS/basix' && ( github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') ) && matrix.python-version == '3.11' }} run: | echo "VERSION_NAME=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV - name: Build documentation to upload if: ${{ github.repository == 'FEniCS/basix' && ( github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') ) && matrix.python-version == '3.11' }} run: | export BASIX_VERSION=`python3 -c "import basix; print(basix.__version__)"` cd doc/web python make_html.py --url https://docs.fenicsproject.org/basix/${{ env.VERSION_NAME }} - name: Checkout FEniCS/docs if: ${{ github.repository == 'FEniCS/basix' && ( github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') ) && matrix.python-version == '3.11' }} uses: actions/checkout@v4 with: repository: "FEniCS/docs" path: "docs" ssh-key: "${{ secrets.SSH_GITHUB_DOCS_PRIVATE_KEY }}" - name: Copy documentation into repository if: ${{ github.repository == 'FEniCS/basix' && ( github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') ) && matrix.python-version == '3.11' }} run: | cd docs git rm -r --ignore-unmatch basix/${{ env.VERSION_NAME }} mkdir -p basix/${{ env.VERSION_NAME }} cp -r ../doc/web/html/* basix/${{ env.VERSION_NAME }} - name: Commit and push documentation to FEniCS/docs if: ${{ github.repository == 'FEniCS/basix' && ( github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') ) && matrix.python-version == '3.11' }} run: | cd docs git config --global user.email "fenics@github.com" git config --global user.name "FEniCS GitHub Actions" git add --all git commit --allow-empty -m "Update Basix docs FEniCS/basix@${{ github.sha }}" git push isolated-python-build: name: Build Python using '--no-build-isolation' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.12' - name: Install dependencies run: sudo apt-get install -y libopenblas-dev liblapack-dev ninja-build - name: Install Python dependencies run: pip install nanobind scikit-build-core[pyproject] - name: Install Basix run: pip install --no-build-isolation . build-cmake: name: Build using C++ and Python parts separately and run tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.12' - name: Install dependencies run: sudo apt-get install -y libopenblas-dev liblapack-dev - name: Install Basix C++ library run: | cd cpp cmake -DCMAKE_BUILD_TYPE=Release -B build-dir -S . # Use make (not ninja) cmake --build build-dir sudo cmake --install build-dir - name: Install Basix Python wrapper run: | cd python pip install .[test] - name: Run units tests run: | pip install pytest-xdist # in ci extras, but most not needed. pytest -n auto --durations 20 test/ - name: Run Python demos run: pytest demo/python/test.py - name: Run C++ demos run: pytest demo/cpp/test.py build-cpp-only: name: Build C++ only and run demos runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install dependencies run: sudo apt-get install -y libopenblas-dev liblapack-dev ninja-build - name: Install Basix run: | cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -B build-dir -S cpp cmake --build build-dir sudo cmake --install build-dir - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.12" - name: Run tests run: | pip install pytest pytest demo/cpp/test.py fenics-basix-0.9.0/.github/workflows/redhat.yml000066400000000000000000000022331470517546000214730ustar00rootroot00000000000000name: Red Hat clone # This workflow will test Basix on Red Hat on: schedule: # '*' is a special character in YAML, so string must be quoted - cron: "0 2 * * TUE" pull_request: branches: - main push: branches: - "main" merge_group: branches: - main workflow_dispatch: ~ jobs: build: name: Build and test (Red Hat) runs-on: ubuntu-latest container: rockylinux/rockylinux:9 env: CC: gcc CXX: g++ steps: - name: Install dependencies run: | dnf -y update dnf install -y dnf-plugins-core dnf config-manager --set-enabled crb dnf install -y openblas-devel cmake gcc gcc-c++ git lapack-devel python3 python3-devel python3-pip - name: Upgrade pip run: python3 -m pip install pip --upgrade - uses: actions/checkout@v4 - name: Install Basix run: python3 -m pip install .[test] - name: Run units tests run: python3 -m pytest --durations 20 test/ - name: Run Python demos run: python3 -m pytest demo/python/test.py - name: Run C++ demos run: python3 -m pytest demo/cpp/test.py fenics-basix-0.9.0/.github/workflows/sonarcloud.yml000066400000000000000000000052621470517546000224020ustar00rootroot00000000000000name: SonarCloud on: push: branches: - main pull_request: branches: - main jobs: build: name: Build runs-on: ubuntu-22.04 if: (github.event.pull_request.head.repo.full_name == github.repository) || github.ref_name == 'main' env: SONAR_SCANNER_VERSION: 5.0.1.3006 # Find the latest version at: # https://github.com/SonarSource/sonar-scanner-cli/tags SONAR_SERVER_URL: "https://sonarcloud.io" BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed PETSC_ARCH: linux-gnu-real-32 steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y libopenblas-dev liblapack-dev ninja-build - name: Set up JDK uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 21 - name: Cache SonarCloud packages uses: actions/cache@v3 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - name: Download and set up sonar-scanner env: SONAR_SCANNER_DOWNLOAD_URL: https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${{ env.SONAR_SCANNER_VERSION }}-linux.zip run: | mkdir -p $HOME/.sonar wget -O $HOME/.sonar/sonar-scanner.zip ${{ env.SONAR_SCANNER_DOWNLOAD_URL }} unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/ echo "$HOME/.sonar/sonar-scanner-${{ env.SONAR_SCANNER_VERSION }}-linux/bin" >> $GITHUB_PATH - name: Download and set up build-wrapper env: BUILD_WRAPPER_DOWNLOAD_URL: ${{ env.SONAR_SERVER_URL }}/static/cpp/build-wrapper-linux-x86.zip run: | wget -O $HOME/.sonar/build-wrapper-linux-x86.zip ${{ env.BUILD_WRAPPER_DOWNLOAD_URL }} unzip -o $HOME/.sonar/build-wrapper-linux-x86.zip -d $HOME/.sonar/ echo "$HOME/.sonar/build-wrapper-linux-x86" >> $GITHUB_PATH - name: Run build-wrapper run: | mkdir build cmake -S ./cpp -B build build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} cmake --build build/ --config Release - name: Run sonar-scanner env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: sonar-scanner --define sonar.host.url="${{ env.SONAR_SERVER_URL }}" --define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}" fenics-basix-0.9.0/.github/workflows/spack.yml000066400000000000000000000037021470517546000213270ustar00rootroot00000000000000name: Spack install on: # Uncomment the below 'push' to trigger on push # push: # branches: # - "**" schedule: # '*' is a special character in YAML, so string must be quoted - cron: "0 2 * * TUE" workflow_dispatch: inputs: spack_repo: description: "Spack repository to test" default: "spack/spack" type: string spack_ref: description: "Spack repository branch/tag to test" default: "develop" type: string basix_version: description: "Basix version" default: "main" type: string jobs: build: runs-on: ubuntu-latest container: ubuntu:latest steps: - name: Install Spack requirements run: | apt-get -y update apt-get install -y bzip2 curl file git gzip make patch python3-minimal tar xz-utils apt-get install -y g++ gfortran # compilers - name: Get Spack if: github.event_name != 'workflow_dispatch' uses: actions/checkout@v4 with: path: ./spack repository: spack/spack - name: Get Spack if: github.event_name == 'workflow_dispatch' uses: actions/checkout@v4 with: path: ./spack repository: ${{ github.event.inputs.spack_repo }} ref: ${{ github.event.inputs.spack_ref }} - name: Install Basix and run tests if: github.event_name != 'workflow_dispatch' run: | . ./spack/share/spack/setup-env.sh spack env create main spack env activate main spack add py-fenics-basix spack install --test=root - name: Install Basix and run tests if: github.event_name == 'workflow_dispatch' run: | . ./spack/share/spack/setup-env.sh spack env create main spack env activate main spack add py-fenics-basix@${{ github.event.inputs.basix_version }} spack install --test=root fenics-basix-0.9.0/.github/workflows/windows.yml000066400000000000000000000071021470517546000217160ustar00rootroot00000000000000name: Basix CI on Windows on: pull_request: branches: - main push: tags: - "v*" branches: - main merge_group: branches: - main workflow_dispatch: jobs: build-combined: name: Combined build and test runs-on: windows-2022 env: VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" steps: - uses: actions/checkout@v4 - name: Export GitHub Actions cache environment variables uses: actions/github-script@v6 with: script: | core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.11" - name: Install build dependencies (workaround) run: | python -m pip install scikit-build-core[pyproject] git+https://github.com/jhale/nanobind.git@jhale/msvc2022-workaround setuptools wheel - name: Install Basix (combined) run: | python -m pip -v install --no-build-isolation --no-cache-dir .[ci] --config-settings=cmake.args=-DINSTALL_RUNTIME_DEPENDENCIES=ON --config-settings=cmake.args=-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake - name: Run units tests run: | python -m pytest -n auto --durations 20 test/ # C++ development is not supported against combined install on Windows - name: Run python demos run: python -m pytest demo/python/test.py build-split: name: Split build and test runs-on: windows-2022 env: VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" steps: - uses: actions/checkout@v4 - name: Insert add_dll_directory calls working-directory: python/basix run: | (Get-Content __init__.py).Replace('# WINDOWSDLL', 'import os; os.add_dll_directory("D:/a/basix/install/bin")') | Set-Content __init__.py Get-Content __init__.py - name: Export GitHub Actions cache environment variables uses: actions/github-script@v6 with: script: | core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.12" - name: Install Basix (C++) run: | cd cpp cmake -DINSTALL_RUNTIME_DEPENDENCIES=ON -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -B build-dir -S . cmake --build build-dir --config Release cmake --install build-dir --config Release --prefix D:/a/basix/install echo "D:/a/basix/install/bin" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8 - name: Install build dependencies (workaround) run: | python -m pip install scikit-build-core[pyproject] git+https://github.com/jhale/nanobind.git@jhale/msvc2022-workaround setuptools wheel - name: Install Basix (Python) run: | cd python python -m pip -v install --no-build-isolation --no-cache-dir .[ci] --config-settings=cmake.args=-DBasix_DIR=D:/a/basix/install/lib/cmake/basix cd ../ - name: Run units tests run: | python -m pytest -n auto --durations 20 test/ - name: Run C++ demos run: python -m pytest demo/cpp/test.py --cmake-args="-DBasix_DIR=D:/a/basix/install/lib/cmake/basix" fenics-basix-0.9.0/.gitignore000066400000000000000000000007651470517546000161040ustar00rootroot00000000000000*.pyc *~ *.so # Currently autogenerated by scikit-build MANIFEST cpp/Makefile cpp/CMakeCache.txt cpp/CMakeFiles cpp/cmake_install.cmake cpp/basix/version.h doc/python/build doc/python/source/generated python/testing_build/ doc/cpp/html doc/cpp/latex doc/web/html doc/web/_temp doc/web/python doc/web/cpp doc/web/web doc/python/source/_autosummary demo/python/*.py.rst demo/cpp/*/_build build*/ joss/paper.pdf .*.swp *.egg-info _skbuild dist *.png *.jpg *.svg !joss/img/*.png !joss/img/*.svg fenics-basix-0.9.0/CITATION.cff000066400000000000000000000013061470517546000157760ustar00rootroot00000000000000cff-version: 1.2.0 message: If you use this software, please cite it as below. license: MIT url: https://github.com/FEniCS/basix preferred-citation: type: article authors: - family-names: Scroggs given-names: Matthew W. orcid: 0000-0002-4658-2443 - family-names: Baratta given-names: Igor A. orcid: 0000-0003-4298-2973 - family-names: Richardson given-names: Chris N. orcid: 0000-0003-3137-1392 - family-names: Wells given-names: Garth N. orcid: 0000-0001-5291-7951 doi: 10.21105/joss.03982 journal: Journal of Open Source Software title: "Basix: a runtime finite element basis evaluation library" volume: 7 issue: 73 start: 3982 month: 5 year: 2022 fenics-basix-0.9.0/CMakeLists.txt000066400000000000000000000002431470517546000166430ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.21) project(BasixFull VERSION "0.9.0" LANGUAGES CXX) set(BASIX_FULL_SKBUILD TRUE) add_subdirectory(cpp) add_subdirectory(python) fenics-basix-0.9.0/CODE_OF_CONDUCT.md000066400000000000000000000072501470517546000167070ustar00rootroot00000000000000Code of Conduct =============== Our Pledge ---------- In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. Our Standards ------------- Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others’ private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting Our Responsibilities -------------------- Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. Scope ----- This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. Enforcement ----------- Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at fenics-steering-council@googlegroups.com. Alternatively, you may report individually to one of the members of the Steering Council. Complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project’s leadership. If you feel that your report has not been followed up satisfactorily, then you may contact our parent organisation NumFOCUS at info@numfocus.org for further redress. Attribution ----------- This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html. Adaptations ----------- * Allow reporting to individual Steering Council members * Added the option to contact NumFOCUS for further redress. For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faqfenics-basix-0.9.0/CONTRIBUTING.md000066400000000000000000000021071470517546000163350ustar00rootroot00000000000000## How to contribute ### Reporting bugs If you find a bug in Basix, please report it on the [GitHub issue tracker](https://github.com/fenics/basix/issues/new?labels=bug). ### Suggesting enhancements If you want to suggest a new feature or an improvement of a current feature, you can submit this on the [issue tracker](https://github.com/fenics/basix/issues). ### Submitting a pull request If you want to directly submit code to Basix, you can do this by forking the repo, then submitting a pull request. If you want to contribute, but are unsure where to start, have a look at the [issues labelled "good first issue"](https://github.com/FEniCS/basix/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). On opening a pull request, unit tests will run on GitHub CI. You can click on these in the pull request to see where (if anywhere) the tests are failing. ### Code of conduct We expect all our contributors to follow the [code of conduct](CODE_OF_CONDUCT.md). Any unacceptable behaviour can be reported to the FEniCS steering council (fenics-steering-council@googlegroups.com). fenics-basix-0.9.0/INSTALL.md000066400000000000000000000033111470517546000155320ustar00rootroot00000000000000# Installation ## Standard Basix can be installed using ```console pip install . ``` ## Advanced It is also possible to install the C++ and Python interfaces separately (see below). This is useful if you only need the C++ interface or during development. ### C++ library In the `cpp/` directory: ```console cmake -DCMAKE_BUILD_TYPE=Release -B build-dir -S . cmake --build build-dir cmake --install build-dir ``` Using the CMake build type `Release` is strongly recommended for performance. ### Python interface After installing the C++ library, install the Python interface by running in the directory `python/`: ```console pip install . ``` For a debug and editable build for development: ```console pip -v install --check-build-dependencies --config-settings=build-dir="build" --config-settings=cmake.build-type="Debug" --config-settings=install.strip=false --no-build-isolation -e . ``` When using the `--no-build-isolation` option all build and runtime dependencies must already be installed. ## Running the unit tests To install Basix and the extra dependencies required to run the Python unit tests: ```console pip install .[test] ``` From the directory `python/` the tests can be run with: ```console pytest test/ ``` ## Dependencies ### C++ Basix requires a C++20 compiler and depends on BLAS and LAPACK. ### Python When using the standard install approach all build and runtime dependencies for the C++ and Python parts of Basix are fetched automatically. Basix specifies sets of optional extras `docs`, `lint`, `optional`, `test`, and `ci` for building documentation, linting, enabling optional features, testing and for continuous integration, respectively, e.g.: ```console pip install .[docs,lint] ``` fenics-basix-0.9.0/LICENSE000066400000000000000000000020571470517546000151150ustar00rootroot00000000000000MIT License Copyright (c) 2020 FEniCS Project Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. fenics-basix-0.9.0/README.md000066400000000000000000000145431470517546000153720ustar00rootroot00000000000000# Basix [![Basix CI](https://github.com/FEniCS/basix/actions/workflows/pythonapp.yml/badge.svg)](https://github.com/FEniCS/basix/actions/workflows/pythonapp.yml) [![Spack install](https://github.com/FEniCS/basix/actions/workflows/spack.yml/badge.svg)](https://github.com/FEniCS/basix/actions/workflows/spack.yml) Basix is a finite element definition and tabulation runtime library. Basix allows users to: - evaluate finite element basis functions and their derivatives at a set of points; - access geometric and topological information about reference cells; - apply push forward and pull back operations to map data between a reference cell and a physical cell; - permute and transform DOFs to allow higher-order elements to be use on arbitrary meshes; and - interpolate into and between finite element spaces. Basix includes a range of built-in elements, and also allows the user to define their own custom elements. Basix is one of the components of FEniCSx, alongside [UFL](https://github.com/fenics/ufl), [FFCx](https://github.com/fenics/ffcx), and [DOLFINx](https://github.com/fenics/dolfinx). ## Installation To install Basix: ```console pip install fenics-basix ``` We currently build binary wheels for Linux and macOS x86-64 architectures. For advanced and developer installation instructions see the more detailed [install instructions](INSTALL.md) ## Documentation Documentation of Basix can be found at https://docs.fenicsproject.org/basix/main/. ## Support If you find a bug in Basix, you can report it on the [GitHub issue tracker](https://github.com/fenics/basix/issues/new?labels=bug). Questions about using Basix can be asked on the [FEniCS discourse group](https://fenicsproject.discourse.group/). ## Contributing Information about how to contribute to Basix can be found [here](CONTRIBUTING.md). ## Supported elements ### Interval In Basix, the sub-entities of the reference interval are numbered as follows: ![The numbering of a reference interval](joss/img/interval_numbering.png) The following elements are supported on an interval: - [Lagrange](https://defelement.com/elements/lagrange.html) - [Bubble](https://defelement.com/elements/bubble.html) - [Serendipity](https://defelement.com/elements/serendipity.html) - [Hermite](https://defelement.com/elements/hermite.html) - [iso](https://defelement.com/elements/p1-iso-p2.html) ### Triangle In Basix, the sub-entities of the reference triangle are numbered as follows: ![The numbering of a reference triangle](joss/img/triangle_numbering.png) The following elements are supported on a triangle: - [Lagrange](https://defelement.com/elements/lagrange.html) - [Nédélec first kind](https://defelement.com/elements/nedelec1.html) - [Raviart-Thomas](https://defelement.com/elements/raviart-thomas.html) - [Nédélec second kind](https://defelement.com/elements/nedelec2.html) - [Brezzi-Douglas-Marini](https://defelement.com/elements/brezzi-douglas-marini.html) - [Regge](https://defelement.com/elements/regge.html) - [Hellan-Herrmann-Johnson](https://defelement.com/elements/hellan-hermann-johnson.html) - [Crouzeix-Raviart](https://defelement.com/elements/crouzeix-raviart.html) - [Bubble](https://defelement.com/elements/bubble.html) - [Hermite](https://defelement.com/elements/hermite.html) - [iso](https://defelement.com/elements/p1-iso-p2.html) ### Quadrilateral In Basix, the sub-entities of the reference quadrilateral are numbered as follows: ![The numbering of a reference quadrilateral](joss/img/quadrilateral_numbering.png) The following elements are supported on a quadrilateral: - [Lagrange](https://defelement.com/elements/lagrange.html) - [Nédélec first kind](https://defelement.com/elements/nedelec1.html) - [Raviart-Thomas](https://defelement.com/elements/qdiv.html) - [Nédélec second kind](https://defelement.com/elements/scurl.html) - [Brezzi-Douglas-Marini](https://defelement.com/elements/sdiv.html) - [Bubble](https://defelement.com/elements/bubble.html) - [DPC](https://defelement.com/elements/dpc.html) - [Serendipity](https://defelement.com/elements/serendipity.html) - [iso](https://defelement.com/elements/p1-iso-p2.html) ### Tetrahedron In Basix, the sub-entities of the reference tetrahedron are numbered as follows: ![The numbering of a reference tetrahedron](joss/img/tetrahedron_numbering.png) The following elements are supported on a tetrahedron: - [Lagrange](https://defelement.com/elements/lagrange.html) - [Nédélec first kind](https://defelement.com/elements/nedelec1.html) - [Raviart-Thomas](https://defelement.com/elements/raviart-thomas.html) - [Nédélec second kind](https://defelement.com/elements/nedelec2.html) - [Brezzi-Douglas-Marini](https://defelement.com/elements/brezzi-douglas-marini.html) - [Regge](https://defelement.com/elements/regge.html) - [Crouzeix-Raviart](https://defelement.com/elements/crouzeix-raviart.html) - [Bubble](https://defelement.com/elements/bubble.html) - [Hermite](https://defelement.com/elements/hermite.html) - [iso](https://defelement.com/elements/p1-iso-p2.html) ### Hexahedron In Basix, the sub-entities of the reference hexahedron are numbered as follows: ![The numbering of a reference hexahedron](joss/img/hexahedron_numbering.png) The following elements are supported on a hexahedron: - [Lagrange](https://defelement.com/elements/lagrange.html) - [Nédélec first kind](https://defelement.com/elements/nedelec1.html) - [Raviart-Thomas](https://defelement.com/elements/qdiv.html) - [Nédélec second kind](https://defelement.com/elements/scurl.html) - [Brezzi-Douglas-Marini](https://defelement.com/elements/sdiv.html) - [Bubble](https://defelement.com/elements/bubble.html) - [DPC](https://defelement.com/elements/dpc.html) - [Serendipity](https://defelement.com/elements/serendipity.html) - [iso](https://defelement.com/elements/p1-iso-p2.html) ### Prism In Basix, the sub-entities of the reference prism are numbered as follows: ![The numbering of a reference prism](joss/img/prism_numbering.png) The following elements are supported on a prism: - [Lagrange](https://defelement.com/elements/lagrange.html) ### Pyramid In Basix, the sub-entities of the reference pyramid are numbered as follows: ![The numbering of a reference pyramid](joss/img/pyramid_numbering.png) The following elements are supported on a pyramid: - [Lagrange](https://defelement.com/elements/lagrange.html) fenics-basix-0.9.0/basix/000077500000000000000000000000001470517546000152125ustar00rootroot00000000000000fenics-basix-0.9.0/basix/_basixcpp.pyi000066400000000000000000000377321470517546000177210ustar00rootroot00000000000000from collections.abc import Sequence import enum from typing import Annotated, overload from numpy.typing import ArrayLike class CellType(enum.IntEnum): point = 0 interval = 1 triangle = 2 tetrahedron = 3 quadrilateral = 4 hexahedron = 5 prism = 6 pyramid = 7 class DPCVariant(enum.IntEnum): unset = 0 simplex_equispaced = 1 simplex_gll = 2 horizontal_equispaced = 3 horizontal_gll = 4 diagonal_equispaced = 5 diagonal_gll = 6 legendre = 7 class ElementFamily(enum.IntEnum): custom = 0 P = 1 BDM = 4 RT = 2 N1E = 3 N2E = 5 Regge = 7 HHJ = 11 bubble = 9 serendipity = 10 DPC = 8 CR = 6 Hermite = 12 iso = 13 class FiniteElement_float32: def tabulate(self, arg0: int, arg1: Annotated[ArrayLike, dict(dtype='float32', writable=False, shape=(None, None), order='C')], /) -> Annotated[ArrayLike, dict(dtype='float32', )]: ... def __eq__(self, arg: object, /) -> bool: ... def hash(self) -> int: ... def permute_subentity_closure(self, arg0: Annotated[ArrayLike, dict(dtype='int32', shape=(None), order='C')], arg1: int, arg2: CellType, /) -> Annotated[ArrayLike, dict(dtype='int32', )]: ... def permute_subentity_closure_inv(self, arg0: Annotated[ArrayLike, dict(dtype='int32', shape=(None), order='C')], arg1: int, arg2: CellType, /) -> Annotated[ArrayLike, dict(dtype='int32', )]: ... def push_forward(self, arg0: Annotated[ArrayLike, dict(dtype='float32', writable=False, shape=(None, None, None), order='C')], arg1: Annotated[ArrayLike, dict(dtype='float32', writable=False, shape=(None, None, None), order='C')], arg2: Annotated[ArrayLike, dict(dtype='float32', writable=False, shape=(None), order='C')], arg3: Annotated[ArrayLike, dict(dtype='float32', writable=False, shape=(None, None, None), order='C')], /) -> Annotated[ArrayLike, dict(dtype='float32', )]: ... def pull_back(self, arg0: Annotated[ArrayLike, dict(dtype='float32', writable=False, shape=(None, None, None), order='C')], arg1: Annotated[ArrayLike, dict(dtype='float32', writable=False, shape=(None, None, None), order='C')], arg2: Annotated[ArrayLike, dict(dtype='float32', writable=False, shape=(None), order='C')], arg3: Annotated[ArrayLike, dict(dtype='float32', writable=False, shape=(None, None, None), order='C')], /) -> Annotated[ArrayLike, dict(dtype='float32', )]: ... def T_apply(self, arg0: Annotated[ArrayLike, dict(dtype='float32', shape=(None), order='C')], arg1: int, arg2: int, /) -> None: ... def Tt_apply_right(self, arg0: Annotated[ArrayLike, dict(dtype='float32', shape=(None), order='C')], arg1: int, arg2: int, /) -> None: ... def Tt_inv_apply(self, arg0: Annotated[ArrayLike, dict(dtype='float32', shape=(None), order='C')], arg1: int, arg2: int, /) -> None: ... def base_transformations(self) -> Annotated[ArrayLike, dict(dtype='float32', )]: ... def entity_transformations(self) -> dict: ... def get_tensor_product_representation(self) -> list[list[FiniteElement_float32]]: ... @property def degree(self) -> int: ... @property def embedded_superdegree(self) -> int: ... @property def embedded_subdegree(self) -> int: ... @property def cell_type(self) -> CellType: ... @property def polyset_type(self) -> PolysetType: ... @property def dim(self) -> int: ... @property def num_entity_dofs(self) -> list[list[int]]: ... @property def entity_dofs(self) -> list[list[list[int]]]: ... @property def num_entity_closure_dofs(self) -> list[list[int]]: ... @property def entity_closure_dofs(self) -> list[list[list[int]]]: ... @property def value_size(self) -> int: ... @property def value_shape(self) -> list[int]: ... @property def discontinuous(self) -> bool: ... @property def family(self) -> ElementFamily: ... @property def lagrange_variant(self) -> LagrangeVariant: ... @property def dpc_variant(self) -> DPCVariant: ... @property def dof_transformations_are_permutations(self) -> bool: ... @property def dof_transformations_are_identity(self) -> bool: ... @property def interpolation_is_identity(self) -> bool: ... @property def map_type(self) -> MapType: ... @property def sobolev_space(self) -> SobolevSpace: ... @property def points(self) -> Annotated[ArrayLike, dict(dtype='float32', writable=False, shape=(None, None), )]: ... @property def interpolation_matrix(self) -> Annotated[ArrayLike, dict(dtype='float32', writable=False, shape=(None, None), )]: ... @property def dual_matrix(self) -> Annotated[ArrayLike, dict(dtype='float32', writable=False, shape=(None, None), )]: ... @property def coefficient_matrix(self) -> Annotated[ArrayLike, dict(dtype='float32', writable=False, shape=(None, None), )]: """Coefficient matrix.""" @property def wcoeffs(self) -> Annotated[ArrayLike, dict(dtype='float32', writable=False, shape=(None, None), )]: ... @property def M(self) -> list[list[Annotated[ArrayLike, dict(dtype='float32', writable=False, )]]]: ... @property def x(self) -> list[list[Annotated[ArrayLike, dict(dtype='float32', writable=False, )]]]: ... @property def has_tensor_product_factorisation(self) -> bool: ... @property def interpolation_nderivs(self) -> int: ... @property def dof_ordering(self) -> list[int]: ... @property def dtype(self) -> str: ... class FiniteElement_float64: def tabulate(self, arg0: int, arg1: Annotated[ArrayLike, dict(dtype='float64', writable=False, shape=(None, None), order='C')], /) -> Annotated[ArrayLike, dict(dtype='float64', )]: ... def __eq__(self, arg: object, /) -> bool: ... def hash(self) -> int: ... def permute_subentity_closure(self, arg0: Annotated[ArrayLike, dict(dtype='int32', shape=(None), order='C')], arg1: int, arg2: CellType, /) -> Annotated[ArrayLike, dict(dtype='int32', )]: ... def permute_subentity_closure_inv(self, arg0: Annotated[ArrayLike, dict(dtype='int32', shape=(None), order='C')], arg1: int, arg2: CellType, /) -> Annotated[ArrayLike, dict(dtype='int32', )]: ... def push_forward(self, arg0: Annotated[ArrayLike, dict(dtype='float64', writable=False, shape=(None, None, None), order='C')], arg1: Annotated[ArrayLike, dict(dtype='float64', writable=False, shape=(None, None, None), order='C')], arg2: Annotated[ArrayLike, dict(dtype='float64', writable=False, shape=(None), order='C')], arg3: Annotated[ArrayLike, dict(dtype='float64', writable=False, shape=(None, None, None), order='C')], /) -> Annotated[ArrayLike, dict(dtype='float64', )]: ... def pull_back(self, arg0: Annotated[ArrayLike, dict(dtype='float64', writable=False, shape=(None, None, None), order='C')], arg1: Annotated[ArrayLike, dict(dtype='float64', writable=False, shape=(None, None, None), order='C')], arg2: Annotated[ArrayLike, dict(dtype='float64', writable=False, shape=(None), order='C')], arg3: Annotated[ArrayLike, dict(dtype='float64', writable=False, shape=(None, None, None), order='C')], /) -> Annotated[ArrayLike, dict(dtype='float64', )]: ... def T_apply(self, arg0: Annotated[ArrayLike, dict(dtype='float64', shape=(None), order='C')], arg1: int, arg2: int, /) -> None: ... def Tt_apply_right(self, arg0: Annotated[ArrayLike, dict(dtype='float64', shape=(None), order='C')], arg1: int, arg2: int, /) -> None: ... def Tt_inv_apply(self, arg0: Annotated[ArrayLike, dict(dtype='float64', shape=(None), order='C')], arg1: int, arg2: int, /) -> None: ... def base_transformations(self) -> Annotated[ArrayLike, dict(dtype='float64', )]: ... def entity_transformations(self) -> dict: ... def get_tensor_product_representation(self) -> list[list[FiniteElement_float64]]: ... @property def degree(self) -> int: ... @property def embedded_superdegree(self) -> int: ... @property def embedded_subdegree(self) -> int: ... @property def cell_type(self) -> CellType: ... @property def polyset_type(self) -> PolysetType: ... @property def dim(self) -> int: ... @property def num_entity_dofs(self) -> list[list[int]]: ... @property def entity_dofs(self) -> list[list[list[int]]]: ... @property def num_entity_closure_dofs(self) -> list[list[int]]: ... @property def entity_closure_dofs(self) -> list[list[list[int]]]: ... @property def value_size(self) -> int: ... @property def value_shape(self) -> list[int]: ... @property def discontinuous(self) -> bool: ... @property def family(self) -> ElementFamily: ... @property def lagrange_variant(self) -> LagrangeVariant: ... @property def dpc_variant(self) -> DPCVariant: ... @property def dof_transformations_are_permutations(self) -> bool: ... @property def dof_transformations_are_identity(self) -> bool: ... @property def interpolation_is_identity(self) -> bool: ... @property def map_type(self) -> MapType: ... @property def sobolev_space(self) -> SobolevSpace: ... @property def points(self) -> Annotated[ArrayLike, dict(dtype='float64', writable=False, shape=(None, None), )]: ... @property def interpolation_matrix(self) -> Annotated[ArrayLike, dict(dtype='float64', writable=False, shape=(None, None), )]: ... @property def dual_matrix(self) -> Annotated[ArrayLike, dict(dtype='float64', writable=False, shape=(None, None), )]: ... @property def coefficient_matrix(self) -> Annotated[ArrayLike, dict(dtype='float64', writable=False, shape=(None, None), )]: """Coefficient matrix.""" @property def wcoeffs(self) -> Annotated[ArrayLike, dict(dtype='float64', writable=False, shape=(None, None), )]: ... @property def M(self) -> list[list[Annotated[ArrayLike, dict(dtype='float64', writable=False, )]]]: ... @property def x(self) -> list[list[Annotated[ArrayLike, dict(dtype='float64', writable=False, )]]]: ... @property def has_tensor_product_factorisation(self) -> bool: ... @property def interpolation_nderivs(self) -> int: ... @property def dof_ordering(self) -> list[int]: ... @property def dtype(self) -> str: ... class LagrangeVariant(enum.IntEnum): unset = 0 equispaced = 1 gll_warped = 2 gll_isaac = 3 gll_centroid = 4 chebyshev_warped = 5 chebyshev_isaac = 6 chebyshev_centroid = 7 gl_warped = 8 gl_isaac = 9 gl_centroid = 10 legendre = 11 bernstein = 12 class LatticeSimplexMethod(enum.IntEnum): none = 0 warp = 1 isaac = 2 centroid = 3 class LatticeType(enum.IntEnum): equispaced = 0 gll = 1 chebyshev = 2 gl = 4 class MapType(enum.IntEnum): identity = 0 L2Piola = 1 covariantPiola = 2 contravariantPiola = 3 doubleCovariantPiola = 4 doubleContravariantPiola = 5 class PolynomialType(enum.IntEnum): legendre = 0 bernstein = 1 class PolysetType(enum.IntEnum): standard = 0 macroedge = 1 class QuadratureType(enum.IntEnum): Default = 0 gauss_jacobi = 1 gll = 2 xiao_gimbutas = 3 class SobolevSpace(enum.IntEnum): L2 = 0 H1 = 1 H2 = 2 H3 = 3 HInf = 8 HDiv = 10 HCurl = 11 HEin = 12 HDivDiv = 13 def cell_facet_jacobians(arg: CellType, /) -> Annotated[ArrayLike, dict(dtype='float64', )]: ... def cell_facet_normals(arg: CellType, /) -> Annotated[ArrayLike, dict(dtype='float64', )]: ... def cell_facet_orientations(arg: CellType, /) -> list[int]: ... def cell_facet_outward_normals(arg: CellType, /) -> Annotated[ArrayLike, dict(dtype='float64', )]: ... def cell_facet_reference_volumes(arg: CellType, /) -> Annotated[ArrayLike, dict(dtype='float64', )]: ... def cell_volume(arg: CellType, /) -> float: ... @overload def compute_interpolation_operator(arg0: FiniteElement_float32, arg1: FiniteElement_float32, /) -> Annotated[ArrayLike, dict(dtype='float32', )]: ... @overload def compute_interpolation_operator(arg0: FiniteElement_float64, arg1: FiniteElement_float64, /) -> Annotated[ArrayLike, dict(dtype='float64', )]: ... def create_custom_element_float32(cell_type: CellType, value_shape: Sequence[int], wcoeffs: Annotated[ArrayLike, dict(dtype='float32', writable=False, shape=(None, None), order='C')], x: Sequence[Sequence[Annotated[ArrayLike, dict(dtype='float32', writable=False, shape=(None, None), order='C')]]], M: Sequence[Sequence[Annotated[ArrayLike, dict(dtype='float32', writable=False, shape=(None, None, None, None), order='C')]]], interpolation_nderivs: int, map_type: MapType, sobolev_space: SobolevSpace, discontinuous: bool, embedded_subdegree: int, embedded_superdegree: int, poly_type: PolysetType) -> FiniteElement_float32: ... def create_custom_element_float64(cell_type: CellType, value_shape: Sequence[int], wcoeffs: Annotated[ArrayLike, dict(dtype='float64', writable=False, shape=(None, None), order='C')], x: Sequence[Sequence[Annotated[ArrayLike, dict(dtype='float64', writable=False, shape=(None, None), order='C')]]], M: Sequence[Sequence[Annotated[ArrayLike, dict(dtype='float64', writable=False, shape=(None, None, None, None), order='C')]]], interpolation_nderivs: int, map_type: MapType, sobolev_space: SobolevSpace, discontinuous: bool, embedded_subdegree: int, embedded_superdegree: int, poly_type: PolysetType) -> FiniteElement_float64: ... def create_element(arg0: ElementFamily, arg1: CellType, arg2: int, arg3: LagrangeVariant, arg4: DPCVariant, arg5: bool, arg6: Sequence[int], arg7: str, /) -> FiniteElement_float32 | FiniteElement_float64: ... def create_lattice(arg0: CellType, arg1: int, arg2: LatticeType, arg3: bool, arg4: LatticeSimplexMethod, /) -> Annotated[ArrayLike, dict(dtype='float64', )]: ... def create_tp_element(arg0: ElementFamily, arg1: CellType, arg2: int, arg3: LagrangeVariant, arg4: DPCVariant, arg5: bool, arg6: str, /) -> FiniteElement_float32 | FiniteElement_float64: ... def geometry(arg: CellType, /) -> Annotated[ArrayLike, dict(dtype='float64', )]: ... @overload def index(arg: int, /) -> int: ... @overload def index(arg0: int, arg1: int, /) -> int: ... @overload def index(arg0: int, arg1: int, arg2: int, /) -> int: ... def make_quadrature(arg0: QuadratureType, arg1: CellType, arg2: PolysetType, arg3: int, /) -> tuple[Annotated[ArrayLike, dict(dtype='float64', )], Annotated[ArrayLike, dict(dtype='float64', )]]: ... def polynomials_dim(arg0: PolynomialType, arg1: CellType, arg2: int, /) -> int: ... def restriction(arg0: PolysetType, arg1: CellType, arg2: CellType, /) -> PolysetType: ... def sobolev_space_intersection(arg0: SobolevSpace, arg1: SobolevSpace, /) -> SobolevSpace: ... def sub_entity_connectivity(arg: CellType, /) -> list[list[list[list[int]]]]: ... def sub_entity_geometry(arg0: CellType, arg1: int, arg2: int, /) -> Annotated[ArrayLike, dict(dtype='float64', )]: ... def superset(arg0: CellType, arg1: PolysetType, arg2: PolysetType, /) -> PolysetType: ... @overload def tabulate_polynomial_set(celltype: CellType, polytype: PolysetType, d: int, n: int, x: Annotated[ArrayLike, dict(dtype='float32', writable=False, shape=(None, None), order='C')]) -> Annotated[ArrayLike, dict(dtype='float32', )]: ... @overload def tabulate_polynomial_set(celltype: CellType, polytype: PolysetType, d: int, n: int, x: Annotated[ArrayLike, dict(dtype='float64', writable=False, shape=(None, None), order='C')]) -> Annotated[ArrayLike, dict(dtype='float64', )]: ... def tabulate_polynomials(arg0: PolynomialType, arg1: CellType, arg2: int, arg3: Annotated[ArrayLike, dict(dtype='float64', writable=False, shape=(None, None), order='C')], /) -> Annotated[ArrayLike, dict(dtype='float64', )]: ... def topology(arg: CellType, /) -> list[list[list[int]]]: ... def tp_dof_ordering(arg0: ElementFamily, arg1: CellType, arg2: int, arg3: LagrangeVariant, arg4: DPCVariant, arg5: bool, /) -> list[int]: ... def tp_factors(arg0: ElementFamily, arg1: CellType, arg2: int, arg3: LagrangeVariant, arg4: DPCVariant, arg5: bool, arg6: Sequence[int], arg7: str, /) -> list[list[FiniteElement_float32]] | list[list[FiniteElement_float64]]: ... fenics-basix-0.9.0/cpp/000077500000000000000000000000001470517546000146665ustar00rootroot00000000000000fenics-basix-0.9.0/cpp/BasixConfig.cmake.in000066400000000000000000000002341470517546000204700ustar00rootroot00000000000000@PACKAGE_INIT@ include(CMakeFindDependencyMacro) if(NOT TARGET @PROJECT_NAME@) include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") endif() fenics-basix-0.9.0/cpp/CMakeLists.txt000066400000000000000000000153171470517546000174350ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.21) # Set the version project(Basix VERSION "0.9.0" LANGUAGES CXX) include(GNUInstallDirs) if(SKBUILD AND UNIX) # Avoid lib64/ which can lead to linking issues. set(CMAKE_INSTALL_LIBDIR basix/lib) endif() if (SKBUILD) # Make sure includes end up inside basix/ subdirectory in wheel. set(CMAKE_INSTALL_INCLUDEDIR basix/include) # Make sure all binaries end up inside basix/ subdirectory in wheel. set(CMAKE_INSTALL_BINDIR basix) endif() include(FeatureSummary) # Options option(BUILD_SHARED_LIBS "Build Basix with shared libraries." ON) add_feature_info(BUILD_SHARED_LIBS BUILD_SHARED_LIBS "Build Basix with shared libraries.") option(INSTALL_RUNTIME_DEPENDENCIES "Include runtime dependencies in install (Windows-only)" OFF) add_feature_info(INSTALL_RUNTIME_DEPENDENCIES INSTALL_RUNTIME_DEPENDENCIES "Include runtime dependencies in install (Windows-only)") find_package(BLAS REQUIRED) find_package(LAPACK REQUIRED) feature_summary(WHAT ALL) if (WIN32) # Windows requires all symbols to be manually exported. # This flag exports all symbols automatically, as in Unix. set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE) endif() # Source files add_library(basix) # Set the C++ standard target_compile_features(basix PUBLIC cxx_std_20) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/basix/version.h.in basix/version.h) target_include_directories(basix PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) set(HEADERS_basix ${CMAKE_CURRENT_SOURCE_DIR}/basix/cell.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/dof-transformations.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/element-families.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/finite-element.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/indexing.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/interpolation.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/lattice.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/maps.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/math.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/moments.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/polynomials.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/polyset.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/precompute.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/quadrature.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/sobolev-spaces.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/e-lagrange.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/e-nce-rtc.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/e-brezzi-douglas-marini.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/e-nedelec.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/e-raviart-thomas.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/e-regge.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/e-hhj.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/e-crouzeix-raviart.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/e-bubble.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/e-serendipity.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/e-hermite.h ${CMAKE_CURRENT_SOURCE_DIR}/basix/mdspan.hpp ${CMAKE_CURRENT_BINARY_DIR}/basix/version.h) target_sources(basix PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/basix/cell.cpp ${CMAKE_CURRENT_SOURCE_DIR}/basix/dof-transformations.cpp ${CMAKE_CURRENT_SOURCE_DIR}/basix/finite-element.cpp ${CMAKE_CURRENT_SOURCE_DIR}/basix/interpolation.cpp ${CMAKE_CURRENT_SOURCE_DIR}/basix/lattice.cpp ${CMAKE_CURRENT_SOURCE_DIR}/basix/moments.cpp ${CMAKE_CURRENT_SOURCE_DIR}/basix/polynomials.cpp ${CMAKE_CURRENT_SOURCE_DIR}/basix/polyset.cpp ${CMAKE_CURRENT_SOURCE_DIR}/basix/precompute.cpp ${CMAKE_CURRENT_SOURCE_DIR}/basix/quadrature.cpp ${CMAKE_CURRENT_SOURCE_DIR}/basix/sobolev-spaces.cpp ${CMAKE_CURRENT_SOURCE_DIR}/basix/e-lagrange.cpp ${CMAKE_CURRENT_SOURCE_DIR}/basix/e-nce-rtc.cpp ${CMAKE_CURRENT_SOURCE_DIR}/basix/e-brezzi-douglas-marini.cpp ${CMAKE_CURRENT_SOURCE_DIR}/basix/e-nedelec.cpp ${CMAKE_CURRENT_SOURCE_DIR}/basix/e-raviart-thomas.cpp ${CMAKE_CURRENT_SOURCE_DIR}/basix/e-regge.cpp ${CMAKE_CURRENT_SOURCE_DIR}/basix/e-hhj.cpp ${CMAKE_CURRENT_SOURCE_DIR}/basix/e-crouzeix-raviart.cpp ${CMAKE_CURRENT_SOURCE_DIR}/basix/e-bubble.cpp ${CMAKE_CURRENT_SOURCE_DIR}/basix/e-hermite.cpp ${CMAKE_CURRENT_SOURCE_DIR}/basix/e-serendipity.cpp) # Configure the library set_target_properties(basix PROPERTIES PUBLIC_HEADER basix/finite-element.h) set_target_properties(basix PROPERTIES PRIVATE_HEADER "${HEADERS_basix}") target_include_directories(basix PUBLIC $ "$") target_link_libraries(basix PRIVATE BLAS::BLAS) target_link_libraries(basix PRIVATE LAPACK::LAPACK) if (UNIX) list(APPEND BASIX_DEVELOPER_FLAGS -O2;-g;-pipe) list(APPEND BASIX_COMPILER_FLAGS -Wall;-Werror;-Wextra;-Wno-comment;-pedantic;) target_compile_options(basix PRIVATE "$<$,$>:${BASIX_COMPILER_FLAGS}>") target_compile_options(basix PRIVATE $<$:${BASIX_DEVELOPER_FLAGS}>) endif() if (MSVC) # M_PI etc. are not not in the standard-compliant MSVC headers. target_compile_definitions(basix PUBLIC _USE_MATH_DEFINES) endif() # TODO: This if can be removed once all platforms have CMake 3.21. if (WIN32) install(TARGETS basix EXPORT BasixTargets RUNTIME_DEPENDENCY_SET dependencies PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} PRIVATE_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/basix RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT RuntimeExecutables LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Development ) if (INSTALL_RUNTIME_DEPENDENCIES) LIST(APPEND PRE_EXCLUDE_REGEXES "api-ms-.*") LIST(APPEND PRE_EXCLUDE_REGEXES "ext-ms-.*") install(RUNTIME_DEPENDENCY_SET dependencies DESTINATION ${CMAKE_INSTALL_BINDIR} PRE_EXCLUDE_REGEXES ${PRE_EXCLUDE_REGEXES} POST_EXCLUDE_REGEXES ".*system32/.*\\.dll" ) endif() else() install(TARGETS basix EXPORT BasixTargets PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} PRIVATE_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/basix RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT RuntimeExecutables LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Development ) endif() # Configure CMake helpers include(CMakePackageConfigHelpers) write_basic_package_version_file(BasixConfigVersion.cmake VERSION ${PACKAGE_VERSION} COMPATIBILITY AnyNewerVersion) configure_package_config_file(BasixConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/BasixConfig.cmake INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/basix) # Install CMake files install(FILES ${CMAKE_CURRENT_BINARY_DIR}/BasixConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/BasixConfigVersion.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/basix COMPONENT Development) install(EXPORT BasixTargets FILE BasixTargets.cmake NAMESPACE Basix:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/basix) fenics-basix-0.9.0/cpp/basix/000077500000000000000000000000001470517546000157745ustar00rootroot00000000000000fenics-basix-0.9.0/cpp/basix/cell.cpp000066400000000000000000000563461470517546000174350ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson // FEniCS Project // SPDX-License-Identifier: MIT #include "cell.h" #include "math.h" #include "mdspan.hpp" #include #include #include using namespace basix; template using mdspan_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>; //----------------------------------------------------------------------------- template std::pair, std::array> cell::geometry(cell::type celltype) { switch (celltype) { case cell::type::point: return {{}, {0, 1}}; case cell::type::interval: return {{0.0, 1.0}, {2, 1}}; case cell::type::triangle: return {{0.0, 0.0, 1.0, 0.0, 0.0, 1.0}, {3, 2}}; case cell::type::quadrilateral: return {{0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0}, {4, 2}}; case cell::type::tetrahedron: return {{0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0}, {4, 3}}; case cell::type::prism: return {{0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0}, {6, 3}}; case cell::type::pyramid: return {{0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0}, {5, 3}}; case cell::type::hexahedron: return {{0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0}, {8, 3}}; default: throw std::runtime_error("Unsupported cell type"); } } //----------------------------------------------------------------------------- std::vector>> cell::topology(cell::type celltype) { switch (celltype) { case cell::type::point: return {{{0}}}; case cell::type::interval: return {{{0}, {1}}, {{0, 1}}}; case cell::type::triangle: { std::vector>> t(3); // Vertices t[0] = {{0}, {1}, {2}}; // Edges t[1] = {{1, 2}, {0, 2}, {0, 1}}; // Cell t[2] = {{0, 1, 2}}; return t; } case cell::type::quadrilateral: { std::vector>> t(3); // Vertices t[0] = {{0}, {1}, {2}, {3}}; // Edges t[1] = {{0, 1}, {0, 2}, {1, 3}, {2, 3}}; // Cell t[2] = {{0, 1, 2, 3}}; return t; } case cell::type::tetrahedron: { std::vector>> t(4); // Vertices t[0] = {{0}, {1}, {2}, {3}}; // Edges t[1] = {{2, 3}, {1, 3}, {1, 2}, {0, 3}, {0, 2}, {0, 1}}; // Faces t[2] = {{1, 2, 3}, {0, 2, 3}, {0, 1, 3}, {0, 1, 2}}; // Cell t[3] = {{0, 1, 2, 3}}; return t; } case cell::type::prism: { std::vector>> t(4); // Vertices t[0] = {{0}, {1}, {2}, {3}, {4}, {5}}; // Edges t[1] = {{0, 1}, {0, 2}, {0, 3}, {1, 2}, {1, 4}, {2, 5}, {3, 4}, {3, 5}, {4, 5}}; // Faces t[2] = {{0, 1, 2}, {0, 1, 3, 4}, {0, 2, 3, 5}, {1, 2, 4, 5}, {3, 4, 5}}; // Cell t[3] = {{0, 1, 2, 3, 4, 5}}; return t; } case cell::type::pyramid: { std::vector>> t(4); // Vertices t[0] = {{0}, {1}, {2}, {3}, {4}}; // Edges t[1] = {{0, 1}, {0, 2}, {0, 4}, {1, 3}, {1, 4}, {2, 3}, {2, 4}, {3, 4}}; // Faces t[2] = {{0, 1, 2, 3}, {0, 1, 4}, {0, 2, 4}, {1, 3, 4}, {2, 3, 4}}; // Cell t[3] = {{0, 1, 2, 3, 4}}; return t; } case cell::type::hexahedron: { std::vector>> t(4); // Vertices t[0] = {{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}}; // Edges t[1] = {{0, 1}, {0, 2}, {0, 4}, {1, 3}, {1, 5}, {2, 3}, {2, 6}, {3, 7}, {4, 5}, {4, 6}, {5, 7}, {6, 7}}; // Faces t[2] = {{0, 1, 2, 3}, {0, 1, 4, 5}, {0, 2, 4, 6}, {1, 3, 5, 7}, {2, 3, 6, 7}, {4, 5, 6, 7}}; // Cell t[3] = {{0, 1, 2, 3, 4, 5, 6, 7}}; return t; } default: throw std::runtime_error("Unsupported cell type"); } } //----------------------------------------------------------------------------- std::vector>>> cell::sub_entity_connectivity(cell::type celltype) { switch (celltype) { case cell::type::point: return {{{{0}}}}; case cell::type::interval: { std::vector>>> t(2); // Vertices t[0] = {{{0}, {0}}, {{1}, {0}}}; // Edge t[1] = {{{0, 1}, {0}}}; return t; } case cell::type::triangle: { std::vector>>> t(3); // Vertices t[0] = {{{0}, {1, 2}, {0}}, {{1}, {0, 2}, {0}}, {{2}, {0, 1}, {0}}}; // Edges t[1] = {{{1, 2}, {0}, {0}}, {{0, 2}, {1}, {0}}, {{0, 1}, {2}, {0}}}; // Face t[2] = {{{0, 1, 2}, {0, 1, 2}, {0}}}; return t; } case cell::type::quadrilateral: { std::vector>>> t(3); // Vertices t[0] = {{{0}, {0, 1}, {0}}, {{1}, {0, 2}, {0}}, {{2}, {1, 3}, {0}}, {{3}, {2, 3}, {0}}}; // Edges t[1] = {{{0, 1}, {0}, {0}}, {{0, 2}, {1}, {0}}, {{1, 3}, {2}, {0}}, {{2, 3}, {3}, {0}}}; // Face t[2] = {{{0, 1, 2, 3}, {0, 1, 2, 3}, {0}}}; return t; } case cell::type::tetrahedron: { std::vector>>> t(4); // Vertices t[0] = {{{0}, {3, 4, 5}, {1, 2, 3}, {0}}, {{1}, {1, 2, 5}, {0, 2, 3}, {0}}, {{2}, {0, 2, 4}, {0, 1, 3}, {0}}, {{3}, {0, 1, 3}, {0, 1, 2}, {0}}}; // Edges t[1] = { {{2, 3}, {0}, {0, 1}, {0}}, {{1, 3}, {1}, {0, 2}, {0}}, {{1, 2}, {2}, {0, 3}, {0}}, {{0, 3}, {3}, {1, 2}, {0}}, {{0, 2}, {4}, {1, 3}, {0}}, {{0, 1}, {5}, {2, 3}, {0}}, }; // Faces t[2] = {{{1, 2, 3}, {0, 1, 2}, {0}, {0}}, {{0, 2, 3}, {0, 3, 4}, {1}, {0}}, {{0, 1, 3}, {1, 3, 5}, {2}, {0}}, {{0, 1, 2}, {2, 4, 5}, {3}, {0}}}; // Volume t[3] = {{{0, 1, 2, 3}, {0, 1, 2, 3, 4, 5}, {0, 1, 2, 3}, {0}}}; return t; } case cell::type::hexahedron: { std::vector>>> t(4); // Vertices t[0] = { {{0}, {0, 1, 2}, {0, 1, 2}, {0}}, {{1}, {0, 3, 4}, {0, 1, 3}, {0}}, {{2}, {1, 5, 6}, {0, 2, 4}, {0}}, {{3}, {3, 5, 7}, {0, 3, 4}, {0}}, {{4}, {2, 8, 9}, {1, 2, 5}, {0}}, {{5}, {4, 8, 10}, {1, 3, 5}, {0}}, {{6}, {6, 9, 11}, {2, 4, 5}, {0}}, {{7}, {7, 10, 11}, {3, 4, 5}, {0}}}; // Edges t[1] = {{{0, 1}, {0}, {0, 1}, {0}}, {{0, 2}, {1}, {0, 2}, {0}}, {{0, 4}, {2}, {1, 2}, {0}}, {{1, 3}, {3}, {0, 3}, {0}}, {{1, 5}, {4}, {1, 3}, {0}}, {{2, 3}, {5}, {0, 4}, {0}}, {{2, 6}, {6}, {2, 4}, {0}}, {{3, 7}, {7}, {3, 4}, {0}}, {{4, 5}, {8}, {1, 5}, {0}}, {{4, 6}, {9}, {2, 5}, {0}}, {{5, 7}, {10}, {3, 5}, {0}}, {{6, 7}, {11}, {4, 5}, {0}}}; // Faces t[2] = {{{0, 1, 2, 3}, {0, 1, 3, 5}, {0}, {0}}, {{0, 1, 4, 5}, {0, 2, 4, 8}, {1}, {0}}, {{0, 2, 4, 6}, {1, 2, 6, 9}, {2}, {0}}, {{1, 3, 5, 7}, {3, 4, 7, 10}, {3}, {0}}, {{2, 3, 6, 7}, {5, 6, 7, 11}, {4}, {0}}, {{4, 5, 6, 7}, {8, 9, 10, 11}, {5}, {0}}}; // Volume t[3] = {{{0, 1, 2, 3, 4, 5, 6, 7}, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, {0, 1, 2, 3, 4, 5}, {0}}}; return t; } case cell::type::prism: { std::vector>>> t(4); // Vertices t[0] = {{{0}, {0, 1, 2}, {0, 1, 2}, {0}}, {{1}, {0, 3, 4}, {0, 1, 3}, {0}}, {{2}, {1, 3, 5}, {0, 2, 3}, {0}}, {{3}, {2, 6, 7}, {1, 2, 4}, {0}}, {{4}, {4, 6, 8}, {1, 3, 4}, {0}}, {{5}, {5, 7, 8}, {2, 3, 4}, {0}}}; // Edges t[1] = {{{0, 1}, {0}, {0, 1}, {0}}, {{0, 2}, {1}, {0, 2}, {0}}, {{0, 3}, {2}, {1, 2}, {0}}, {{1, 2}, {3}, {0, 3}, {0}}, {{1, 4}, {4}, {1, 3}, {0}}, {{2, 5}, {5}, {2, 3}, {0}}, {{3, 4}, {6}, {1, 4}, {0}}, {{3, 5}, {7}, {2, 4}, {0}}, {{4, 5}, {8}, {3, 4}, {0}}}; // Faces t[2] = {{{0, 1, 2}, {0, 1, 3}, {0}, {0}}, {{0, 1, 3, 4}, {0, 2, 4, 6}, {1}, {0}}, {{0, 2, 3, 5}, {1, 2, 5, 7}, {2}, {0}}, {{1, 2, 4, 5}, {3, 4, 5, 8}, {3}, {0}}, {{3, 4, 5}, {6, 7, 8}, {4}, {0}}}; // Volume t[3] = {{{0, 1, 2, 3, 4, 5}, {0, 1, 2, 3, 4, 5, 6, 7, 8}, {0, 1, 2, 3, 4}, {0}}}; return t; } case cell::type::pyramid: { std::vector>>> t(4); // Vertices t[0] = {{{0}, {0, 1, 2}, {0, 1, 2}, {0}}, {{1}, {0, 3, 4}, {0, 1, 3}, {0}}, {{2}, {1, 5, 6}, {0, 2, 4}, {0}}, {{3}, {3, 5, 7}, {0, 3, 4}, {0}}, {{4}, {2, 4, 6, 7}, {1, 2, 3, 4}, {0}}}; // Edges t[1] = {{{0, 1}, {0}, {0, 1}, {0}}, {{0, 2}, {1}, {0, 2}, {0}}, {{0, 4}, {2}, {1, 2}, {0}}, {{1, 3}, {3}, {0, 3}, {0}}, {{1, 4}, {4}, {1, 3}, {0}}, {{2, 3}, {5}, {0, 4}, {0}}, {{2, 4}, {6}, {2, 4}, {0}}, {{3, 4}, {7}, {3, 4}, {0}}}; // Faces t[2] = {{{0, 1, 2, 3}, {0, 1, 3, 5}, {0}, {0}}, {{0, 1, 4}, {0, 2, 4}, {1}, {0}}, {{0, 2, 4}, {1, 2, 6}, {2}, {0}}, {{1, 3, 4}, {3, 4, 7}, {3}, {0}}, {{2, 3, 4}, {5, 6, 7}, {4}, {0}}}; // Volume t[3] = {{{0, 1, 2, 3, 4}, {0, 1, 2, 3, 4, 5, 6, 7}, {0, 1, 2, 3, 4}, {0}}}; return t; } default: throw std::runtime_error("Unsupported cell type"); } } //----------------------------------------------------------------------------- int cell::topological_dimension(cell::type cell_type) { switch (cell_type) { case cell::type::point: return 0; case cell::type::interval: return 1; case cell::type::triangle: return 2; case cell::type::quadrilateral: return 2; case cell::type::tetrahedron: return 3; case cell::type::hexahedron: return 3; case cell::type::prism: return 3; case cell::type::pyramid: return 3; default: throw std::runtime_error("Unsupported cell type"); } } //----------------------------------------------------------------------------- template std::pair, std::array> cell::sub_entity_geometry(cell::type celltype, int dim, int index) { const std::vector>> cell_topology = cell::topology(celltype); if (dim < 0 or dim >= (int)cell_topology.size()) throw std::runtime_error("Invalid dimension for sub-entity"); const std::vector>& t = cell_topology[dim]; if (index < 0 or index >= (int)t.size()) throw std::runtime_error("Invalid entity index"); const auto [cell_geometry, shape] = cell::geometry(celltype); mdspan_t geometry(cell_geometry.data(), shape); std::array subshape = {t[index].size(), geometry.extent(1)}; std::vector sub_geometry(subshape[0] * subshape[1]); mdspan_t sub_entity(sub_geometry.data(), subshape); for (std::size_t i = 0; i < sub_entity.extent(0); ++i) for (std::size_t j = 0; j < sub_entity.extent(1); ++j) sub_entity(i, j) = geometry(t[index][i], j); return {sub_geometry, subshape}; } //---------------------------------------------------------------------------- int cell::num_sub_entities(cell::type celltype, int dim) { constexpr std::array point = {1, 0, 0, 0}; constexpr std::array interval = {2, 1, 0, 0}; constexpr std::array triangle = {3, 3, 1, 0}; constexpr std::array tetrahedron = {4, 6, 4, 1}; constexpr std::array quadrilateral = {4, 4, 1, 0}; constexpr std::array hexahedron = {8, 12, 6, 1}; constexpr std::array prism = {6, 9, 5, 1}; constexpr std::array pyramid = {5, 8, 5, 1}; switch (celltype) { case cell::type::point: return point[dim]; case cell::type::interval: return interval[dim]; case cell::type::triangle: return triangle[dim]; case cell::type::tetrahedron: return tetrahedron[dim]; case cell::type::quadrilateral: return quadrilateral[dim]; case cell::type::hexahedron: return hexahedron[dim]; case cell::type::prism: return prism[dim]; case cell::type::pyramid: return pyramid[dim]; default: throw std::runtime_error("Unsupported cell type"); } } //---------------------------------------------------------------------------- cell::type cell::sub_entity_type(cell::type celltype, int dim, int index) { const int tdim = cell::topological_dimension(celltype); assert(dim >= 0 and dim <= tdim); if (dim == 0) return cell::type::point; else if (dim == 1) return cell::type::interval; else if (dim == tdim) return celltype; const std::vector>> t = cell::topology(celltype); switch (t[dim][index].size()) { case 3: return cell::type::triangle; case 4: return cell::type::quadrilateral; default: throw std::runtime_error("Error in sub_entity_type"); } } //----------------------------------------------------------------------------- template T cell::volume(cell::type cell_type) { switch (cell_type) { case cell::type::point: return 0; case cell::type::interval: return 1; case cell::type::triangle: return 0.5; case cell::type::quadrilateral: return 1; case cell::type::tetrahedron: return 1.0 / 6.0; case cell::type::hexahedron: return 1; case cell::type::prism: return 0.5; case cell::type::pyramid: return 1.0 / 3.0; default: throw std::runtime_error("Unsupported cell type"); } } //----------------------------------------------------------------------------- template std::pair, std::array> cell::facet_outward_normals(cell::type cell_type) { auto [normals, shape] = cell::facet_normals(cell_type); mdspan_t n(normals.data(), shape); std::vector facet_orientations = cell::facet_orientations(cell_type); for (std::size_t f = 0; f < n.extent(0); ++f) { if (facet_orientations[f]) { for (std::size_t k = 0; k < n.extent(1); ++k) n(f, k) = -n(f, k); } } return {normals, shape}; } //----------------------------------------------------------------------------- template std::pair, std::array> cell::facet_normals(cell::type cell_type) { const std::size_t tdim = cell::topological_dimension(cell_type); std::vector> facets = cell::topology(cell_type)[tdim - 1]; auto [xdata, xshape] = cell::geometry(cell_type); mdspan_t x(xdata.data(), xshape); std::array shape = {facets.size(), tdim}; std::vector normal(shape[0] * shape[1]); mdspan_t n(normal.data(), shape); switch (tdim) { case 1: std::ranges::fill(normal, 1.0); return {normal, shape}; case 2: { for (std::size_t f = 0; f < facets.size(); ++f) { const std::vector& facet = facets[f]; assert(facet.size() == 2); n(f, 0) = x(facet[1], 1) - x(facet[0], 1); n(f, 1) = x(facet[0], 0) - x(facet[1], 0); T L = std::sqrt(n(f, 0) * n(f, 0) + n(f, 1) * n(f, 1)); n(f, 0) /= L; n(f, 1) /= L; } return {normal, shape}; } case 3: { for (std::size_t f = 0; f < facets.size(); ++f) { const std::vector& facet = facets[f]; assert(facets[f].size() == 3 or facets[f].size() == 4); std::array e0, e1; for (std::size_t i = 0; i < 3; ++i) { e0[i] = x(facet[1], i) - x(facet[0], i); e1[i] = x(facet[2], i) - x(facet[0], i); } std::array n_f = {e0[1] * e1[2] - e0[2] * e1[1], e0[2] * e1[0] - e0[0] * e1[2], e0[0] * e1[1] - e0[1] * e1[0]}; T L = std::sqrt(n_f[0] * n_f[0] + n_f[1] * n_f[1] + n_f[2] * n_f[2]); for (std::size_t i = 0; i < 3; ++i) n(f, i) = n_f[i] / L; } return {normal, shape}; } default: throw std::runtime_error("Wrong topological dimension"); } } //----------------------------------------------------------------------------- std::vector cell::facet_orientations(cell::type cell_type) { const std::size_t tdim = cell::topological_dimension(cell_type); const auto [_x, xshape] = cell::geometry(cell_type); mdspan_t x(_x.data(), xshape); std::vector> facets = topology(cell_type)[tdim - 1]; const auto [normals, shape] = cell::facet_normals(cell_type); mdspan_t n(normals.data(), shape); std::vector midpoint(x.extent(1), 0.0); for (std::size_t i = 0; i < x.extent(1); ++i) { for (std::size_t j = 0; j < x.extent(0); ++j) midpoint[i] += x(j, i); midpoint[i] /= x.extent(0); } std::vector orientations(n.extent(0)); for (std::size_t f = 0; f < n.extent(0); ++f) { double dot = 0.0; for (std::size_t i = 0; i < n.extent(1); ++i) dot += n(f, i) * (x(facets[f][0], i) - midpoint[i]); orientations[f] = dot < 0; } return orientations; } //----------------------------------------------------------------------------- template std::vector cell::facet_reference_volumes(cell::type cell_type) { int tdim = topological_dimension(cell_type); std::vector facet_types = subentity_types(cell_type)[tdim - 1]; std::vector out; for (auto& facet_type : facet_types) out.push_back(cell::volume(facet_type)); return out; } //----------------------------------------------------------------------------- std::vector> cell::subentity_types(cell::type cell_type) { switch (cell_type) { case cell::type::point: return {{cell::type::point}}; case cell::type::interval: return {{cell::type::point, cell::type::point}, {cell::type::interval}}; case cell::type::triangle: return {{cell::type::point, cell::type::point, cell::type::point}, {cell::type::interval, cell::type::interval, cell::type::interval}, {cell::type::triangle}}; case cell::type::quadrilateral: return {{cell::type::point, cell::type::point, cell::type::point, cell::type::point}, {cell::type::interval, cell::type::interval, cell::type::interval, cell::type::interval}, {cell::type::quadrilateral}}; case cell::type::tetrahedron: return {{cell::type::point, cell::type::point, cell::type::point, cell::type::point}, {cell::type::interval, cell::type::interval, cell::type::interval, cell::type::interval, cell::type::interval, cell::type::interval}, {cell::type::triangle, cell::type::triangle, cell::type::triangle, cell::type::triangle}, {cell::type::tetrahedron}}; case cell::type::hexahedron: return {{cell::type::point, cell::type::point, cell::type::point, cell::type::point, cell::type::point, cell::type::point, cell::type::point, cell::type::point}, {cell::type::interval, cell::type::interval, cell::type::interval, cell::type::interval, cell::type::interval, cell::type::interval, cell::type::interval, cell::type::interval, cell::type::interval, cell::type::interval, cell::type::interval, cell::type::interval}, {cell::type::quadrilateral, cell::type::quadrilateral, cell::type::quadrilateral, cell::type::quadrilateral, cell::type::quadrilateral, cell::type::quadrilateral}, {cell::type::hexahedron}}; case cell::type::prism: return {{cell::type::point, cell::type::point, cell::type::point, cell::type::point, cell::type::point, cell::type::point}, {cell::type::interval, cell::type::interval, cell::type::interval, cell::type::interval, cell::type::interval, cell::type::interval, cell::type::interval, cell::type::interval, cell::type::interval}, {cell::type::triangle, cell::type::quadrilateral, cell::type::quadrilateral, cell::type::quadrilateral, cell::type::triangle}, {cell::type::prism}}; case cell::type::pyramid: return {{cell::type::point, cell::type::point, cell::type::point, cell::type::point, cell::type::point}, {cell::type::interval, cell::type::interval, cell::type::interval, cell::type::interval, cell::type::interval, cell::type::interval, cell::type::interval, cell::type::interval}, {cell::type::quadrilateral, cell::type::triangle, cell::type::triangle, cell::type::triangle, cell::type::triangle}, {cell::type::pyramid}}; default: throw std::runtime_error("Unsupported cell type"); } } //----------------------------------------------------------------------------- template std::pair, std::array> cell::facet_jacobians(cell::type cell_type) { std::size_t tdim = cell::topological_dimension(cell_type); if (tdim != 2 and tdim != 3) { throw std::runtime_error( "Facet jacobians not supported for this cell type."); } const auto [_x, xshape] = cell::geometry(cell_type); mdspan_t x(_x.data(), xshape); std::vector> facets = topology(cell_type)[tdim - 1]; std::array shape = {facets.size(), tdim, tdim - 1}; std::vector jacobians(shape[0] * shape[1] * shape[2]); mdspan_t J(jacobians.data(), shape); for (std::size_t f = 0; f < facets.size(); ++f) { const std::vector& facet = facets[f]; for (std::size_t j = 0; j < tdim - 1; ++j) for (std::size_t k = 0; k < J.extent(1); ++k) J(f, k, j) = x(facet[1 + j], k) - x(facet[0], k); } return {std::move(jacobians), std::move(shape)}; } //----------------------------------------------------------------------------- /// @cond // Explicit instantiation for double and float template std::pair, std::array> cell::geometry(cell::type); template std::pair, std::array> cell::geometry(cell::type); template std::pair, std::array> cell::sub_entity_geometry(cell::type, int, int); template std::pair, std::array> cell::sub_entity_geometry(cell::type, int, int); template float cell::volume(cell::type); template double cell::volume(cell::type); template std::pair, std::array> cell::facet_outward_normals(cell::type); template std::pair, std::array> cell::facet_outward_normals(cell::type); template std::pair, std::array> cell::facet_normals(cell::type); template std::pair, std::array> cell::facet_normals(cell::type); template std::vector cell::facet_reference_volumes(cell::type); template std::vector cell::facet_reference_volumes(cell::type); template std::pair, std::array> cell::facet_jacobians(cell::type); template std::pair, std::array> cell::facet_jacobians(cell::type); /// @endcond //----------------------------------------------------------------------------- fenics-basix-0.9.0/cpp/basix/cell.h000066400000000000000000000110401470517546000170600ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson // FEniCS Project // SPDX-License-Identifier: MIT #pragma once #include #include #include #include /// Information about reference cells /// This namespace include functions that can be used to obtain /// geometric and topological information about reference cells namespace basix::cell { /// Cell type enum class type : int { point = 0, interval = 1, triangle = 2, tetrahedron = 3, quadrilateral = 4, hexahedron = 5, prism = 6, pyramid = 7 }; /// Cell geometry /// @param celltype Cell Type /// @return (0) Vertex point data of the cell and (1) the shape of the /// data array. The points are stored in row-major format and the shape /// is is (npoints, gdim) template std::pair, std::array> geometry(cell::type celltype); /// Cell topology /// @param celltype Cell Type /// @return List of topology (vertex indices) for each dimension (0..tdim) std::vector>> topology(cell::type celltype); /// Get the numbers of entities connected to each subentity of the cell. /// /// Returns a vector of the form: output[dim][entity_n][connected_dim] = /// [connected_entity_n0, connected_entity_n1, ...] This indicates that /// the entity of dimension `dim` and number `entity_n` is connected to /// the entities of dimension `connected_dim` and numbers /// `connected_entity_n0`, `connected_entity_n1`, ... /// /// @param celltype Cell Type /// @return List of topology (vertex indices) for each dimension (0..tdim) std::vector>>> sub_entity_connectivity(cell::type celltype); /// Sub-entity of a cell, given by topological dimension and index /// @param celltype The cell::type /// @param dim Dimension of sub-entity /// @param index Local index of sub-entity /// @return Set of vertex points of the sub-entity. Shape is (npoints, gdim) template std::pair, std::array> sub_entity_geometry(cell::type celltype, int dim, int index); /// Number of sub-entities of a cell by topological dimension /// @param celltype The cell::type /// @param dim Dimension of sub-entity /// @return The number of sub-entities of the given dimension int num_sub_entities(cell::type celltype, int dim); /// Get the topological dimension for a given cell type /// @param celltype Cell type /// @return the topological dimension int topological_dimension(cell::type celltype); /// Get the cell type of a sub-entity of given dimension and index /// @param celltype Type of cell /// @param dim Topological dimension of sub-entity /// @param index Index of sub-entity /// @return cell type of sub-entity cell::type sub_entity_type(cell::type celltype, int dim, int index); /// Get the volume of a reference cell /// @param cell_type Type of cell /// @return The volume of the cell template T volume(cell::type cell_type); /// Get the (outward) normals to the facets of a reference cell /// @param cell_type Type of cell /// @return The outward normals. Shape is (nfacets, gdim) template std::pair, std::array> facet_outward_normals(cell::type cell_type); /// Get the normals to the facets of a reference cell oriented using the /// low-to-high ordering of the facet /// @param cell_type Type of cell /// @return The normals. Shape is (nfacets, gdim) template std::pair, std::array> facet_normals(cell::type cell_type); /// Get an array of bools indicating whether or not the facet normals are /// outward pointing /// @param cell_type Type of cell /// @return The orientations std::vector facet_orientations(cell::type cell_type); /// Get the reference volumes of the facets of a reference cell /// @param cell_type Type of cell /// @return The volumes of the references associated with each facet template std::vector facet_reference_volumes(cell::type cell_type); /// Get the types of the subentities of a reference cell /// @param cell_type Type of cell /// @return The subentity types. Indices are (tdim, entity) std::vector> subentity_types(cell::type cell_type); /// Get the jacobians of the facets of a reference cell /// @param cell_type Type of cell /// @return The jacobians of the facets. Shape is (nfacets, gdim, gdim - 1) template std::pair, std::array> facet_jacobians(cell::type cell_type); } // namespace basix::cell fenics-basix-0.9.0/cpp/basix/dof-transformations.cpp000066400000000000000000000514061470517546000225050ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson & Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #include "dof-transformations.h" #include "math.h" #include "mdspan.hpp" #include "polyset.h" #include #include #include #include #include #include using namespace basix; namespace stdex = MDSPAN_IMPL_STANDARD_NAMESPACE::MDSPAN_IMPL_PROPOSED_NAMESPACE; template using mdarray_t = stdex::mdarray>; template using mdspan_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>; template using map_data_t = std::tuple(std::span)>, mdarray_t, T, mdarray_t>; template using mapinfo_t = std::map>>; namespace { //----------------------------------------------------------------------------- int find_first_subentity(cell::type cell_type, cell::type entity_type) { const int edim = cell::topological_dimension(entity_type); std::vector entities = cell::subentity_types(cell_type)[edim]; if (auto it = std::ranges::find(entities, entity_type); it != entities.end()) { return std::distance(entities.begin(), it); } else throw std::runtime_error("Entity not found"); } //----------------------------------------------------------------------------- template void push_forward(maps::type map_type, Q&& u, const P& U, const R& J, double detJ, const S& K) { switch (map_type) { case maps::type::identity: { assert(U.extent(0) == u.extent(0)); assert(U.extent(1) == u.extent(1)); for (std::size_t i = 0; i < U.extent(0); ++i) for (std::size_t j = 0; j < U.extent(1); ++j) u(i, j) = U(i, j); return; } case maps::type::covariantPiola: maps::covariant_piola(u, U, J, detJ, K); return; case maps::type::contravariantPiola: maps::contravariant_piola(u, U, J, detJ, K); return; case maps::type::doubleCovariantPiola: maps::double_covariant_piola(u, U, J, detJ, K); return; case maps::type::doubleContravariantPiola: maps::double_contravariant_piola(u, U, J, detJ, K); return; default: throw std::runtime_error("Map not implemented"); } } //----------------------------------------------------------------------------- template mapinfo_t get_mapinfo(cell::type cell_type) { switch (cell_type) { case cell::type::point: return mapinfo_t(); case cell::type::interval: return mapinfo_t(); case cell::type::triangle: { mapinfo_t mapinfo; auto& data = mapinfo.try_emplace(cell::type::interval).first->second; auto map = [](auto pt) -> std::array { return {pt[1], pt[0], 0.0}; }; stdex::mdarray> J( stdex::extents{}, {0., 1., 1., 0.}); T detJ = -1.0; stdex::mdarray> K( stdex::extents{}, {0., 1., 1., 0.}); data.push_back(std::tuple(map, J, detJ, K)); return mapinfo; } case cell::type::quadrilateral: { mapinfo_t mapinfo; auto& data = mapinfo.try_emplace(cell::type::interval).first->second; auto map = [](auto pt) -> std::array { return {1 - pt[0], pt[1], 0}; }; stdex::mdarray> J( stdex::extents{}, {-1., 0., 0., 1.}); T detJ = -1.0; stdex::mdarray> K( stdex::extents{}, {-1., 0., 0., 1.}); data.push_back(std::tuple(map, J, detJ, K)); return mapinfo; } case cell::type::tetrahedron: { mapinfo_t mapinfo; { auto& data = mapinfo.try_emplace(cell::type::interval).first->second; auto map = [](auto pt) -> std::array { return {pt[0], pt[2], pt[1]}; }; stdex::mdarray> J( stdex::extents{}, {1., 0., 0., 0., 0., 1., 0., 1., 0.}); T detJ = -1.0; stdex::mdarray> K( stdex::extents{}, {1., 0., 0., 0., 0., 1., 0., 1., 0.}); data.push_back(std::tuple(map, J, detJ, K)); } { auto& data = mapinfo.try_emplace(cell::type::triangle).first->second; { auto map = [](auto pt) -> std::array { return {pt[2], pt[0], pt[1]}; }; stdex::mdarray> J( stdex::extents{}, {0., 1., 0., 0., 0., 1., 1., 0., 0.}); T detJ = 1.0; stdex::mdarray> K( stdex::extents{}, {0., 0., 1., 1., 0., 0., 0., 1., 0.}); data.push_back(std::tuple(map, J, detJ, K)); } { auto map = [](auto pt) -> std::array { return {pt[0], pt[2], pt[1]}; }; stdex::mdarray> J( stdex::extents{}, {1., 0., 0., 0., 0., 1., 0., 1., 0.}); T detJ = -1.0; stdex::mdarray> K( stdex::extents{}, {1., 0., 0., 0., 0., 1., 0., 1., 0.}); data.push_back(std::tuple(map, J, detJ, K)); } } return mapinfo; } case cell::type::hexahedron: { mapinfo_t mapinfo; { auto& data = mapinfo.try_emplace(cell::type::interval).first->second; auto map = [](auto pt) -> std::array { return {1 - pt[0], pt[1], pt[2]}; }; stdex::mdarray> J( stdex::extents{}, {-1., 0., 0., 0., 1., 0., 0., 0., 1.}); T detJ = -1.0; stdex::mdarray> K( stdex::extents{}, {-1., 0., 0., 0., 1., 0., 0., 0., 1.}); data.push_back(std::tuple(map, J, detJ, K)); } { auto& data = mapinfo.try_emplace(cell::type::quadrilateral).first->second; { auto map = [](auto pt) -> std::array { return {1 - pt[1], pt[0], pt[2]}; }; stdex::mdarray> J( stdex::extents{}, {0., 1., 0., -1., 0., 0., 0., 0., 1.}); T detJ = 1.0; stdex::mdarray> K( stdex::extents{}, {0., -1., 0., 1., 0., 0., 0., 0., 1.}); data.push_back(std::tuple(map, J, detJ, K)); } { auto map = [](auto pt) -> std::array { return {pt[1], pt[0], pt[2]}; }; stdex::mdarray> J( stdex::extents{}, {0., 1., 0., 1., 0., 0., 0., 0., 1.}); T detJ = -1.0; stdex::mdarray> K( stdex::extents{}, {0., 1., 0., 1., 0., 0., 0., 0., 1.}); data.push_back(std::tuple(map, J, detJ, K)); } } return mapinfo; } case cell::type::prism: { mapinfo_t mapinfo; { auto& data = mapinfo.try_emplace(cell::type::interval).first->second; auto map = [](auto pt) -> std::array { return {1 - pt[0], pt[1], pt[2]}; }; stdex::mdarray> J( stdex::extents{}, {-1., 0., 0., 0., 1., 0., 0., 0., 1.}); T detJ = -1.0; stdex::mdarray> K( stdex::extents{}, {-1., 0., 0., 0., 1., 0., 0., 0., 1.}); data.push_back(std::tuple(map, J, detJ, K)); } { auto& data = mapinfo.try_emplace(cell::type::triangle).first->second; { auto map = [](auto pt) -> std::array { return {1 - pt[1] - pt[0], pt[0], pt[2]}; }; stdex::mdarray> J( stdex::extents{}, {0., 1., 0., -1., -1., 0., 0., 0., 1.}); T detJ = 1.0; stdex::mdarray> K( stdex::extents{}, {-1., -1., 0., 1., 0., 0., 0., 0., 1.}); data.push_back(std::tuple(map, J, detJ, K)); } { auto map = [](auto pt) -> std::array { return {pt[1], pt[0], pt[2]}; }; stdex::mdarray> J( stdex::extents{}, {0., 1., 0., 1., 0., 0., 0., 0., 1.}); T detJ = -1.; stdex::mdarray> K( stdex::extents{}, {0., 1., 0., 1., 0., 0., 0., 0., 1.}); data.push_back(std::tuple(map, J, detJ, K)); } } { auto& data = mapinfo.try_emplace(cell::type::quadrilateral).first->second; { auto map = [](auto pt) -> std::array { return {1 - pt[2], pt[1], pt[0]}; }; stdex::mdarray> J( stdex::extents{}, {0., 0., 1., 0., 1., 0., -1., 0., 0.}); T detJ = 1.0; stdex::mdarray> K( stdex::extents{}, {0., 0., -1., 0., 1., 0., 1., 0., 0.}); data.push_back(std::tuple(map, J, detJ, K)); } { // scope auto map = [](auto pt) -> std::array { return {pt[2], pt[1], pt[0]}; }; stdex::mdarray> J( stdex::extents{}, {0., 0., 1., 0., 1., 0., 1., 0., 0.}); T detJ = -1.; stdex::mdarray> K( stdex::extents{}, {0., 0., 1., 0., 1., 0., 1., 0., 0.}); data.push_back(std::tuple(map, J, detJ, K)); } } return mapinfo; } case cell::type::pyramid: { mapinfo_t mapinfo; { auto& data = mapinfo.try_emplace(cell::type::interval).first->second; auto map = [](auto pt) -> std::array { return {1 - pt[0], pt[1], pt[2]}; }; stdex::mdarray> J( stdex::extents{}, {-1., 0., 0., 0., 1., 0., 0., 0., 1.}); T detJ = -1.; stdex::mdarray> K( stdex::extents{}, {-1., 0., 0., 0., 1., 0., 0., 0., 1.}); data.push_back(std::tuple(map, J, detJ, K)); } { auto& data = mapinfo.try_emplace(cell::type::quadrilateral).first->second; { auto map = [](auto pt) -> std::array { return {1 - pt[1], pt[0], pt[2]}; }; stdex::mdarray> J( stdex::extents{}, {0., 1., 0., -1., 0., 0., 0., 0., 1.}); T detJ = 1.; stdex::mdarray> K( stdex::extents{}, {0., -1., 0., 1., 0., 0., 0., 0., 1.}); data.push_back(std::tuple(map, J, detJ, K)); } { auto map = [](auto pt) -> std::array { return {pt[1], pt[0], pt[2]}; }; stdex::mdarray> J( stdex::extents{}, {0., 1., 0., 1., 0., 0., 0., 0., 1.}); T detJ = -1.; stdex::mdarray> K( stdex::extents{}, {0., 1., 0., 1., 0., 0., 0., 0., 1.}); data.push_back(std::tuple(map, J, detJ, K)); } } { auto& data = mapinfo.try_emplace(cell::type::triangle).first->second; { auto map = [](auto pt) -> std::array { return {1 - pt[2] - pt[0], pt[1], pt[0]}; }; stdex::mdarray> J( stdex::extents{}, {0., 0., 1., 0., 1., 0., -1., 0., -1.}); T detJ = 1.; stdex::mdarray> K( stdex::extents{}, {-1., 0., -1., 0., 1., 0., 1., 0., 0.}); data.push_back(std::tuple(map, J, detJ, K)); } { auto map = [](auto pt) -> std::array { return {pt[2], pt[1], pt[0]}; }; stdex::mdarray> J( stdex::extents{}, {0., 0., 1., 0., 1., 0., 1., 0., 0.}); T detJ = -1.; stdex::mdarray> K( stdex::extents{}, {0., 0., 1., 0., 1., 0., 1., 0., 0.}); data.push_back(std::tuple(map, J, detJ, K)); } } return mapinfo; } default: throw std::runtime_error("Unsupported cell type"); } } //----------------------------------------------------------------------------- template std::pair, std::array> compute_transformation( cell::type cell_type, const std::array>, 4>& x, const std::array>, 4>& M, mdspan_t coeffs, const mdarray_t& J, T detJ, const mdarray_t& K, const std::function(std::span)> map_point, int degree, int tdim, int entity, std::size_t vs, const maps::type map_type, const polyset::type ptype) { if (x[tdim].size() == 0 or x[tdim][entity].extent(0) == 0) return {{}, {0, 0}}; mdspan_t pts = x[tdim][entity]; mdspan_t imat = M[tdim][entity]; const std::size_t ndofs = imat.extent(0); const std::size_t npts = pts.extent(0); const int psize = polyset::dim(cell_type, ptype, degree); std::size_t dofstart = 0; for (int d = 0; d < tdim; ++d) for (std::size_t i = 0; i < M[d].size(); ++i) dofstart += M[d][i].extent(0); for (int i = 0; i < entity; ++i) dofstart += M[tdim][i].extent(0); std::size_t total_ndofs = 0; for (int d = 0; d <= 3; ++d) for (std::size_t i = 0; i < M[d].size(); ++i) total_ndofs += M[d][i].extent(0); // Map the points to reverse the edge, then tabulate at those points mdarray_t mapped_pts(pts.extents()); for (std::size_t p = 0; p < mapped_pts.extent(0); ++p) { auto mp = map_point( std::span(pts.data_handle() + p * pts.extent(1), pts.extent(1))); for (std::size_t k = 0; k < mapped_pts.extent(1); ++k) mapped_pts(p, k) = mp[k]; } auto [polyset_vals_b, polyset_shape] = polyset::tabulate( cell_type, ptype, degree, 0, mdspan_t(mapped_pts.data(), mapped_pts.extents())); assert(polyset_shape[0] == 1); mdspan_t polyset_vals(polyset_vals_b.data(), polyset_shape[1], polyset_shape[2]); mdarray_t tabulated_data(npts, total_ndofs, vs); for (std::size_t j = 0; j < vs; ++j) { mdarray_t result(polyset_vals.extent(1), coeffs.extent(0)); for (std::size_t k0 = 0; k0 < coeffs.extent(0); ++k0) for (std::size_t k1 = 0; k1 < polyset_vals.extent(1); ++k1) for (std::size_t k2 = 0; k2 < polyset_vals.extent(0); ++k2) result(k1, k0) += coeffs(k0, k2 + psize * j) * polyset_vals(k2, k1); for (std::size_t k0 = 0; k0 < result.extent(0); ++k0) for (std::size_t k1 = 0; k1 < result.extent(1); ++k1) tabulated_data(k0, k1, j) = result(k0, k1); } // push forward mdarray_t pushed_data(tabulated_data.extents()); { mdarray_t temp_data(pushed_data.extent(1), pushed_data.extent(2)); for (std::size_t i = 0; i < npts; ++i) { mdspan_t tab( tabulated_data.data() + i * tabulated_data.extent(1) * tabulated_data.extent(2), tabulated_data.extent(1), tabulated_data.extent(2)); push_forward(map_type, mdspan_t(temp_data.data(), temp_data.extents()), tab, J, detJ, K); for (std::size_t k0 = 0; k0 < temp_data.extent(0); ++k0) for (std::size_t k1 = 0; k1 < temp_data.extent(1); ++k1) pushed_data(i, k0, k1) = temp_data(k0, k1); } } // Interpolate to calculate coefficients std::vector transformb(ndofs * ndofs); mdspan_t transform(transformb.data(), ndofs, ndofs); for (std::size_t d = 0; d < imat.extent(3); ++d) { for (std::size_t i = 0; i < vs; ++i) { for (std::size_t k0 = 0; k0 < transform.extent(1); ++k0) for (std::size_t k1 = 0; k1 < transform.extent(0); ++k1) for (std::size_t k2 = 0; k2 < imat.extent(2); ++k2) transform(k1, k0) += imat(k0, i, k2, d) * pushed_data(k2, k1 + dofstart, i); } } return {std::move(transformb), {transform.extent(0), transform.extent(1)}}; } } // namespace //----------------------------------------------------------------------------- template std::map, std::array>> doftransforms::compute_entity_transformations( cell::type cell_type, const std::array< std::vector>>, 4>& x, const std::array< std::vector>>, 4>& M, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> coeffs, int degree, std::size_t vs, maps::type map_type, polyset::type ptype) { std::map, std::array>> out; const mapinfo_t mapinfo = get_mapinfo(cell_type); for (auto& [entity_type, emap_data] : mapinfo) { const int tdim = cell::topological_dimension(entity_type); const int entity = find_first_subentity(cell_type, entity_type); std::size_t ndofs = M[tdim].size() == 0 ? 0 : M[tdim][entity].extent(0); std::vector transform; transform.reserve(emap_data.size() * ndofs * ndofs); for (auto& [mapfn, J, detJ, K] : emap_data) { auto [t2b, _] = compute_transformation(cell_type, x, M, coeffs, J, detJ, K, mapfn, degree, tdim, entity, vs, map_type, ptype); transform.insert(transform.end(), t2b.begin(), t2b.end()); } out.try_emplace(entity_type, std::pair(std::move(transform), std::array{emap_data.size(), ndofs, ndofs})); } return out; } //----------------------------------------------------------------------------- /// @cond template std::map, std::array>> doftransforms::compute_entity_transformations( cell::type, const std::array>>, 4>&, const std::array>>, 4>&, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const float, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>, int, std::size_t, maps::type, polyset::type); template std::map, std::array>> doftransforms::compute_entity_transformations( cell::type, const std::array>>, 4>&, const std::array>>, 4>&, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const double, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>, int, std::size_t, maps::type, polyset::type); /// @endcond //----------------------------------------------------------------------------- fenics-basix-0.9.0/cpp/basix/dof-transformations.h000066400000000000000000000041601470517546000221450ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson & Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #pragma once #include "cell.h" #include "maps.h" #include "mdspan.hpp" #include "polyset.h" #include #include #include #include #include /// /// @brief Functions to transform DOFs in high degree Lagrange spaces. /// The functions in this namespace calculate the permutations that can /// be used to rotate and reflect DOF points in Lagrange spaces. namespace basix::doftransforms { /// @brief Compute the entity DOF transformations for an element. /// /// @param[in] cell_type The cell type /// @param[in] x Interpolation points for the element. Indices are /// (tdim, entity index, point index, dim) /// @param[in] M Interpolation matrix for the element. Indices are /// (tdim, entity index, dof, vs, point_index, derivative) /// @param[in] coeffs The coefficients that define the basis functions /// of the element in terms of the orthonormal basis. Shape is /// (dim(Legendre polynomials), dim(finite element polyset)) /// @param[in] degree The degree of the element /// @param[in] vs The value size of the element /// @param[in] map_type The map type used by the element /// @param[in] ptype The polyset type used by the element /// @return Entity transformations. For each cell, the shape is /// (ntransformation, ndofs, ndofs) template std::map, std::array>> compute_entity_transformations( cell::type cell_type, const std::array< std::vector>>, 4>& x, const std::array< std::vector>>, 4>& M, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> coeffs, int degree, std::size_t vs, maps::type map_type, polyset::type ptype); } // namespace basix::doftransforms fenics-basix-0.9.0/cpp/basix/e-brezzi-douglas-marini.cpp000066400000000000000000000104521470517546000231420ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson and Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #include "e-brezzi-douglas-marini.h" #include "e-lagrange.h" #include "e-nedelec.h" #include "element-families.h" #include "maps.h" #include "math.h" #include "mdspan.hpp" #include "moments.h" #include "polyset.h" #include "sobolev-spaces.h" #include using namespace basix; //---------------------------------------------------------------------------- template FiniteElement element::create_bdm(cell::type celltype, int degree, lagrange_variant lvariant, bool discontinuous) { if (celltype != cell::type::triangle and celltype != cell::type::tetrahedron) throw std::runtime_error("Unsupported cell type"); if (degree < 1) throw std::runtime_error("Degree must be at least 1"); const std::size_t tdim = cell::topological_dimension(celltype); std::array>, 4> x; std::array>, 4> M; for (std::size_t i = 0; i < tdim - 1; ++i) { std::size_t num_ent = cell::num_sub_entities(celltype, i); x[i] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[i] = std::vector(num_ent, impl::mdarray_t(0, tdim, 0, 1)); } // Integral moments on facets const cell::type facettype = sub_entity_type(celltype, tdim - 1, 0); const FiniteElement facet_moment_space = create_lagrange(facettype, degree, lvariant, true); { auto [_x, xshape, _M, Mshape] = moments::make_normal_integral_moments( facet_moment_space, celltype, polyset::type::standard, tdim, degree * 2); assert(_x.size() == _M.size()); for (std::size_t i = 0; i < _x.size(); ++i) { x[tdim - 1].emplace_back(std::array{xshape[0], xshape[1]}, _x[i]); M[tdim - 1].emplace_back(Mshape, _M[i]); } } // Integral moments on interior if (degree > 1) { // Interior integral moment auto [_x, xshape, _M, Mshape] = moments::make_dot_integral_moments( create_nedelec(celltype, degree - 1, lvariant, true), celltype, polyset::type::standard, tdim, 2 * degree - 1); assert(_x.size() == _M.size()); for (std::size_t i = 0; i < _x.size(); ++i) { x[tdim].emplace_back(std::array{xshape[0], xshape[1]}, _x[i]); M[tdim].emplace_back(Mshape, _M[i]); } } else { std::size_t num_ent = cell::num_sub_entities(celltype, tdim); x[tdim] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[tdim] = std::vector(num_ent, impl::mdarray_t(0, tdim, 0, 1)); } const std::vector>> topology = cell::topology(celltype); std::array>, 4> xview = impl::to_mdspan(x); std::array>, 4> Mview = impl::to_mdspan(M); std::array>, 4> xbuffer; std::array>, 4> Mbuffer; if (discontinuous) { std::array>, 4> xshape; std::array>, 4> Mshape; std::tie(xbuffer, xshape, Mbuffer, Mshape) = make_discontinuous(xview, Mview, tdim, tdim); xview = impl::to_mdspan(xbuffer, xshape); Mview = impl::to_mdspan(Mbuffer, Mshape); } // The number of order (degree) scalar polynomials const std::size_t ndofs = tdim * polyset::dim(celltype, polyset::type::standard, degree); sobolev::space space = discontinuous ? sobolev::space::L2 : sobolev::space::HDiv; return FiniteElement( family::BDM, celltype, polyset::type::standard, degree, {tdim}, impl::mdspan_t(math::eye(ndofs).data(), ndofs, ndofs), xview, Mview, 0, maps::type::contravariantPiola, space, discontinuous, degree, degree, lvariant, element::dpc_variant::unset); } //----------------------------------------------------------------------------- template FiniteElement element::create_bdm(cell::type, int, lagrange_variant, bool); template FiniteElement element::create_bdm(cell::type, int, lagrange_variant, bool); //----------------------------------------------------------------------------- fenics-basix-0.9.0/cpp/basix/e-brezzi-douglas-marini.h000066400000000000000000000014341470517546000226070ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson & Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #pragma once #include "cell.h" #include "element-families.h" #include "finite-element.h" #include namespace basix::element { /// Create BDM element /// @param[in] celltype The cell type /// @param[in] degree The degree of the element /// @param[in] lvariant The Lagrange variant to use when defining the element to /// take integral moments against /// @param[in] discontinuous Controls whether the element is continuous or /// discontinuous /// @return A finite element template FiniteElement create_bdm(cell::type celltype, int degree, lagrange_variant lvariant, bool discontinuous); } // namespace basix::element fenics-basix-0.9.0/cpp/basix/e-bubble.cpp000066400000000000000000000153271470517546000201650ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson & Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #include "e-bubble.h" #include "element-families.h" #include "lattice.h" #include "maps.h" #include "polyset.h" #include "quadrature.h" #include "sobolev-spaces.h" #include #include using namespace basix; //---------------------------------------------------------------------------- template FiniteElement basix::element::create_bubble(cell::type celltype, int degree, bool discontinuous) { if (discontinuous) throw std::runtime_error("Cannot create a discontinuous bubble element."); switch (celltype) { case cell::type::interval: if (degree < 2) throw std::runtime_error( "Bubble element on an interval must have degree at least 2"); break; case cell::type::triangle: if (degree < 3) throw std::runtime_error( "Bubble element on a triangle must have degree at least 3"); break; case cell::type::tetrahedron: if (degree < 4) throw std::runtime_error( "Bubble element on a tetrahedron must have degree at least 4"); break; case cell::type::quadrilateral: if (degree < 2) throw std::runtime_error("Bubble element on a quadrilateral interval " "must have degree at least 2"); break; case cell::type::hexahedron: if (degree < 2) throw std::runtime_error( "Bubble element on a hexahedron must have degree at least 2"); break; default: throw std::runtime_error("Unsupported cell type"); } const std::size_t tdim = cell::topological_dimension(celltype); std::array>, 4> x; std::array>, 4> M; for (std::size_t i = 0; i < tdim; ++i) { const std::size_t num_ent = cell::num_sub_entities(celltype, i); x[i] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[i] = std::vector(num_ent, impl::mdarray_t(0, 1, 0, 1)); } // Evaluate the expansion polynomials at the quadrature points const auto [_pts, wts] = quadrature::make_quadrature( quadrature::type::Default, celltype, polyset::type::standard, 2 * degree); impl::mdspan_t pts(_pts.data(), wts.size(), _pts.size() / wts.size()); const auto [_phi, shape] = polyset::tabulate(celltype, polyset::type::standard, degree, 0, pts); impl::mdspan_t phi(_phi.data(), shape); // The number of order (degree) polynomials const std::size_t psize = phi.extent(1); // Create points at nodes on interior std::size_t ndofs = 0; { const auto [points, pshape] = lattice::create( celltype, degree, lattice::type::equispaced, false); ndofs = pshape[0]; x[tdim].emplace_back(pshape, points); } auto create_phi1 = [](auto& phi, auto& buffer) { buffer.resize(phi.extent(1) * phi.extent(2)); impl::mdspan_t phi1(buffer.data(), phi.extent(1), phi.extent(2)); for (std::size_t i = 0; i < phi1.extent(0); ++i) for (std::size_t j = 0; j < phi1.extent(1); ++j) phi1(i, j) = phi(0, i, j); return phi1; }; // Create coefficients for order (degree-1) vector polynomials std::vector phi1_buffer; impl::mdspan_t phi1; std::vector bubble; switch (celltype) { case cell::type::interval: { const auto [_phi1, shape] = polyset::tabulate( celltype, polyset::type::standard, degree - 2, 0, pts); impl::mdspan_t p1(_phi1.data(), shape); phi1 = create_phi1(p1, phi1_buffer); for (std::size_t i = 0; i < pts.extent(0); ++i) { T x0 = pts(i, 0); bubble.push_back(x0 * (1.0 - x0)); } break; } case cell::type::triangle: { const auto [_phi1, shape] = polyset::tabulate( celltype, polyset::type::standard, degree - 3, 0, pts); impl::mdspan_t p1(_phi1.data(), shape); phi1 = create_phi1(p1, phi1_buffer); for (std::size_t i = 0; i < pts.extent(0); ++i) { T x0 = pts(i, 0); T x1 = pts(i, 1); bubble.push_back(x0 * x1 * (1.0 - x0 - x1)); } break; } case cell::type::tetrahedron: { const auto [_phi1, shape] = polyset::tabulate( celltype, polyset::type::standard, degree - 4, 0, pts); impl::mdspan_t p1(_phi1.data(), shape); phi1 = create_phi1(p1, phi1_buffer); for (std::size_t i = 0; i < pts.extent(0); ++i) { T x0 = pts(i, 0); T x1 = pts(i, 1); T x2 = pts(i, 2); bubble.push_back(x0 * x1 * x2 * (1 - x0 - x1 - x2)); } break; } case cell::type::quadrilateral: { const auto [_phi1, shape] = polyset::tabulate( celltype, polyset::type::standard, degree - 2, 0, pts); impl::mdspan_t p1(_phi1.data(), shape); phi1 = create_phi1(p1, phi1_buffer); for (std::size_t i = 0; i < pts.extent(0); ++i) { T x0 = pts(i, 0); T x1 = pts(i, 1); bubble.push_back(x0 * (1 - x0) * x1 * (1 - x1)); } break; } case cell::type::hexahedron: { const auto [_phi1, shape] = polyset::tabulate( celltype, polyset::type::standard, degree - 2, 0, pts); impl::mdspan_t p1(_phi1.data(), shape); phi1 = create_phi1(p1, phi1_buffer); for (std::size_t i = 0; i < pts.extent(0); ++i) { T x0 = pts(i, 0); T x1 = pts(i, 1); T x2 = pts(i, 2); bubble.push_back(x0 * (1 - x0) * x1 * (1 - x1) * x2 * (1 - x2)); } break; } default: throw std::runtime_error("Unknown cell type."); } impl::mdarray_t wcoeffs(ndofs, psize); for (std::size_t i = 0; i < phi1.extent(0); ++i) for (std::size_t j = 0; j < psize; ++j) for (std::size_t k = 0; k < wts.size(); ++k) wcoeffs(i, j) += wts[k] * phi1(i, k) * bubble[k] * phi(0, j, k); math::orthogonalise(wcoeffs); auto& _M = M[tdim].emplace_back(ndofs, 1, ndofs, 1); for (std::size_t i = 0; i < _M.extent(0); ++i) _M(i, 0, i, 0) = 1.0; impl::mdspan_t wview(wcoeffs.data(), wcoeffs.extents()); sobolev::space space = discontinuous ? sobolev::space::L2 : sobolev::space::H1; return FiniteElement( element::family::bubble, celltype, polyset::type::standard, degree, {}, wview, impl::to_mdspan(x), impl::to_mdspan(M), 0, maps::type::identity, space, discontinuous, -1, degree, element::lagrange_variant::unset, element::dpc_variant::unset); } //----------------------------------------------------------------------------- template FiniteElement element::create_bubble(cell::type, int, bool); template FiniteElement element::create_bubble(cell::type, int, bool); //----------------------------------------------------------------------------- fenics-basix-0.9.0/cpp/basix/e-bubble.h000066400000000000000000000012011470517546000176140ustar00rootroot00000000000000// Copyright (c) 2020 Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #pragma once #include "cell.h" #include "finite-element.h" #include namespace basix::element { /// Create a bubble element on cell with given degree /// @param[in] celltype The cell type /// @param[in] degree The degree of the element /// @param[in] discontinuous Controls whether the element is continuous or /// discontinuous /// @return A finite element template FiniteElement create_bubble(cell::type celltype, int degree, bool discontinuous); } // namespace basix::element fenics-basix-0.9.0/cpp/basix/e-crouzeix-raviart.cpp000066400000000000000000000072421470517546000222450ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson // FEniCS Project // SPDX-License-Identifier: MIT #include "e-crouzeix-raviart.h" #include "cell.h" #include "element-families.h" #include "maps.h" #include "math.h" #include "mdspan.hpp" #include "sobolev-spaces.h" #include #include using namespace basix; //----------------------------------------------------------------------------- template FiniteElement basix::element::create_cr(cell::type celltype, int degree, bool discontinuous) { if (degree != 1) throw std::runtime_error("Degree must be 1 for Crouzeix-Raviart"); if (celltype != cell::type::triangle and celltype != cell::type::tetrahedron) { throw std::runtime_error( "Crouzeix-Raviart is only defined on triangles and tetrahedra."); } const std::size_t tdim = cell::topological_dimension(celltype); if (tdim < 2) { throw std::runtime_error( "topological dim must be 2 or 3 for Crouzeix-Raviart"); } const std::vector>> topology = cell::topology(celltype); const std::vector>& facet_topology = topology[tdim - 1]; const std::size_t ndofs = facet_topology.size(); const auto [gdata, shape] = cell::geometry(celltype); impl::mdspan_t geometry(gdata.data(), shape); std::array>, 4> x; std::array>, 4> M; for (std::size_t i = 0; i < tdim - 1; ++i) { const std::size_t num_ent = topology[i].size(); x[i] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[i] = std::vector(num_ent, impl::mdarray_t(0, 1, 0, 1)); } x[tdim - 1] = std::vector(facet_topology.size(), impl::mdarray_t(1, tdim)); M[tdim - 1] = std::vector( facet_topology.size(), impl::mdarray_t(std::array{1, 1, 1, 1}, 1)); // Compute facet midpoints for (std::size_t f = 0; f < facet_topology.size(); ++f) { const std::vector& ft = facet_topology[f]; auto& _x = x[tdim - 1][f]; for (std::size_t i = 0; i < ft.size(); ++i) { for (std::size_t j = 0; j < geometry.extent(1); ++j) _x(0, j) += geometry(ft[i], j) / ft.size(); } } x[tdim] = std::vector(topology[tdim].size(), impl::mdarray_t(0, tdim)); M[tdim] = std::vector(topology[tdim].size(), impl::mdarray_t(0, 1, 0, 1)); std::array>, 4> xview = impl::to_mdspan(x); std::array>, 4> Mview = impl::to_mdspan(M); std::array>, 4> xbuffer; std::array>, 4> Mbuffer; if (discontinuous) { std::array>, 4> xshape; std::array>, 4> Mshape; std::tie(xbuffer, xshape, Mbuffer, Mshape) = element::make_discontinuous(xview, Mview, tdim, 1); xview = impl::to_mdspan(xbuffer, xshape); Mview = impl::to_mdspan(Mbuffer, Mshape); } return FiniteElement( element::family::CR, celltype, polyset::type::standard, 1, {}, impl::mdspan_t(math::eye(ndofs).data(), ndofs, ndofs), xview, Mview, 0, maps::type::identity, sobolev::space::L2, discontinuous, degree, degree, element::lagrange_variant::unset, element::dpc_variant::unset); } //----------------------------------------------------------------------------- template FiniteElement element::create_cr(cell::type, int, bool); template FiniteElement element::create_cr(cell::type, int, bool); //----------------------------------------------------------------------------- fenics-basix-0.9.0/cpp/basix/e-crouzeix-raviart.h000066400000000000000000000011701470517546000217040ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson // FEniCS Project // SPDX-License-Identifier: MIT #pragma once #include "cell.h" #include "finite-element.h" #include namespace basix::element { /// Crouzeix-Raviart element /// @note degree must be 1 for Crouzeix-Raviart /// @param[in] celltype The cell type /// @param[in] degree The degree of the element /// @param[in] discontinuous Controls whether the element is continuous or /// discontinuous /// @return A finite element template FiniteElement create_cr(cell::type celltype, int degree, bool discontinuous); } // namespace basix::element fenics-basix-0.9.0/cpp/basix/e-hermite.cpp000066400000000000000000000100011470517546000203470ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson & Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #include "e-hermite.h" #include "element-families.h" #include "lattice.h" #include "maps.h" #include "math.h" #include "mdspan.hpp" #include "polyset.h" #include "quadrature.h" #include "sobolev-spaces.h" #include #include using namespace basix; //---------------------------------------------------------------------------- template FiniteElement basix::element::create_hermite(cell::type celltype, int degree, bool discontinuous) { if (celltype != cell::type::interval and celltype != cell::type::triangle and celltype != cell::type::tetrahedron) { throw std::runtime_error("Unsupported cell type"); } if (degree != 3) throw std::runtime_error("Hermite element must have degree 3"); const std::size_t tdim = cell::topological_dimension(celltype); const std::size_t ndofs = polyset::dim(celltype, polyset::type::standard, degree); const std::vector>> topology = cell::topology(celltype); const auto [gdata, gshape] = cell::geometry(celltype); impl::mdspan_t geometry(gdata.data(), gshape); const std::size_t deriv_count = polyset::nderivs(celltype, 1); std::array>, 4> x; std::array>, 4> M; // Loop over entities of dimension 'dim' for (std::size_t e = 0; e < topology[0].size(); ++e) { const auto [entity_x, entity_shape] = cell::sub_entity_geometry(celltype, 0, e); x[0].emplace_back(entity_shape, entity_x); auto& _M = M[0].emplace_back(1 + tdim, 1, 1, deriv_count); _M(0, 0, 0, 0) = 1; for (std::size_t d = 0; d < tdim; ++d) _M(d + 1, 0, 0, d + 1) = 1; } for (std::size_t e = 0; e < topology[1].size(); ++e) { x[1].emplace_back(0, tdim); M[1].emplace_back(0, 1, 0, deriv_count); } if (tdim >= 2) { for (std::size_t e = 0; e < topology[2].size(); ++e) { auto& _x = x[2].emplace_back(1, tdim); std::vector midpoint(tdim, 0); for (auto p : topology[2][e]) for (std::size_t i = 0; i < geometry.extent(1); ++i) _x(0, i) += geometry(p, i) / topology[2][e].size(); auto& _M = M[2].emplace_back(1, 1, 1, deriv_count); _M(0, 0, 0, 0) = 1; } } if (tdim == 3) { x[3] = std::vector>(topology[2].size(), impl::mdarray_t(0, tdim)); M[3] = std::vector>( topology[2].size(), impl::mdarray_t(0, 1, 0, deriv_count)); } std::array>, 4> xview = impl::to_mdspan(x); std::array>, 4> Mview = impl::to_mdspan(M); std::array>, 4> xbuffer; std::array>, 4> Mbuffer; if (discontinuous) { std::array>, 4> xshape; std::array>, 4> Mshape; std::tie(xbuffer, xshape, Mbuffer, Mshape) = element::make_discontinuous(xview, Mview, tdim, 1); xview = impl::to_mdspan(xbuffer, xshape); Mview = impl::to_mdspan(Mbuffer, Mshape); } sobolev::space space = discontinuous ? sobolev::space::L2 : sobolev::space::H2; return FiniteElement( element::family::Hermite, celltype, polyset::type::standard, degree, {}, impl::mdspan_t(math::eye(ndofs).data(), ndofs, ndofs), xview, Mview, 1, maps::type::identity, space, discontinuous, -1, degree, element::lagrange_variant::unset, element::dpc_variant::unset); } //----------------------------------------------------------------------------- template FiniteElement element::create_hermite(cell::type, int, bool); template FiniteElement element::create_hermite(cell::type, int, bool); //----------------------------------------------------------------------------- fenics-basix-0.9.0/cpp/basix/e-hermite.h000066400000000000000000000012041470517546000200210ustar00rootroot00000000000000// Copyright (c) 2022 Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #pragma once #include "cell.h" #include "finite-element.h" #include namespace basix::element { /// Create a Hermite element on cell with given degree /// @param[in] celltype The cell type /// @param[in] degree The degree of the element /// @param[in] discontinuous Controls whether the element is continuous or /// discontinuous /// @return A finite element template FiniteElement create_hermite(cell::type celltype, int degree, bool discontinuous); } // namespace basix::element fenics-basix-0.9.0/cpp/basix/e-hhj.cpp000066400000000000000000000157321470517546000175030ustar00rootroot00000000000000// Copyright (c) 2022 Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #include "e-hhj.h" #include "e-lagrange.h" #include "element-families.h" #include "maps.h" #include "math.h" #include "polyset.h" #include "quadrature.h" #include "sobolev-spaces.h" #include using namespace basix; //----------------------------------------------------------------------------- template FiniteElement basix::element::create_hhj(cell::type celltype, int degree, bool discontinuous) { if (celltype != cell::type::triangle) throw std::runtime_error("Unsupported celltype"); const std::size_t tdim = cell::topological_dimension(celltype); const int nc = tdim * (tdim + 1) / 2; const int basis_size = polyset::dim(celltype, polyset::type::standard, degree); const std::size_t ndofs = basis_size * nc; const std::size_t psize = basis_size * tdim * tdim; impl::mdarray_t wcoeffs(ndofs, psize); for (std::size_t i = 0; i < tdim; ++i) { for (std::size_t j = 0; j < tdim; ++j) { int xoff = i + tdim * j; int yoff = i + j; if (tdim == 3 and i > 0 and j > 0) ++yoff; const std::size_t s = basis_size; for (std::size_t k = 0; k < s; ++k) wcoeffs(yoff * s + k, xoff * s + k) = i == j ? 1.0 : std::sqrt(0.5); } } const std::vector>> topology = cell::topology(celltype); const auto [gbuffer, gshape] = cell::geometry(celltype); impl::mdspan_t geometry(gbuffer.data(), gshape); std::array>, 4> x; std::array>, 4> M; for (std::size_t e = 0; e < topology[0].size(); ++e) { x[0].emplace_back(0, tdim); M[0].emplace_back(0, tdim * tdim, 0, 1); } // Loop over edge and higher dimension entities for (std::size_t d = 1; d < topology.size(); ++d) { if (static_cast(degree) + 1 < d) { for (std::size_t e = 0; e < topology[d].size(); ++e) { x[d].emplace_back(0, tdim); M[d].emplace_back(0, tdim * tdim, 0, 1); } } else { // Loop over entities of dimension dim for (std::size_t e = 0; e < topology[d].size(); ++e) { // Entity coordinates const auto [entity_x_buffer, eshape] = cell::sub_entity_geometry(celltype, d, e); std::span x0(entity_x_buffer.data(), eshape[1]); impl::mdspan_t entity_x(entity_x_buffer.data(), eshape); // Tabulate points in lattice cell::type ct = cell::sub_entity_type(celltype, d, e); const std::size_t ndofs = polyset::dim(ct, polyset::type::standard, degree + 1 - d); const auto [ptsbuffer, wts] = quadrature::make_quadrature( quadrature::type::Default, ct, polyset::type::standard, degree + (degree + 1 - d)); impl::mdspan_t pts(ptsbuffer.data(), wts.size(), ptsbuffer.size() / wts.size()); FiniteElement moment_space = create_lagrange( ct, degree + 1 - d, element::lagrange_variant::legendre, true); const auto [phib, phishape] = moment_space.tabulate(0, pts); impl::mdspan_t moment_values(phib.data(), phishape); auto& _x = x[d].emplace_back(pts.extent(0), tdim); // Copy points for (std::size_t p = 0; p < pts.extent(0); ++p) { for (std::size_t k = 0; k < _x.extent(1); ++k) _x(p, k) = x0[k]; for (std::size_t k0 = 0; k0 < entity_x.extent(0) - 1; ++k0) for (std::size_t k1 = 0; k1 < _x.extent(1); ++k1) _x(p, k1) += (entity_x(k0 + 1, k1) - x0[k1]) * pts(p, k0); } // Store up outer(t, t) for all tangents const std::vector& vert_ids = topology[d][e]; const std::size_t ntangents = d * (d + 1) / 2; impl::mdarray_t vvt(ntangents, geometry.extent(1), geometry.extent(1)); std::vector edge_t(geometry.extent(1)); int c = 0; for (std::size_t s = 0; s < d; ++s) { for (std::size_t r = s + 1; r < d + 1; ++r) { if (geometry.extent(1) != 2) throw std::runtime_error("Not implemented"); edge_t[0] = geometry(vert_ids[s], 1) - geometry(vert_ids[r], 1); edge_t[1] = geometry(vert_ids[r], 0) - geometry(vert_ids[s], 0); // outer product v.v^T const auto [result, shape] = math::outer(edge_t, edge_t); for (std::size_t k0 = 0; k0 < shape[0]; ++k0) for (std::size_t k1 = 0; k1 < shape[1]; ++k1) vvt(c, k0, k1) = result[k0 * shape[1] + k1]; ++c; } } auto& _M = M[d].emplace_back(ndofs * ntangents, tdim * tdim, pts.extent(0), 1); for (int n = 0; n < moment_space.dim(); ++n) { for (std::size_t j = 0; j < ntangents; ++j) { std::vector vvt_flat; for (std::size_t k0 = 0; k0 < vvt.extent(1); ++k0) for (std::size_t k1 = 0; k1 < vvt.extent(2); ++k1) vvt_flat.push_back(vvt(j, k0, k1)); for (std::size_t q = 0; q < pts.extent(0); ++q) { for (std::size_t i = 0; i < tdim * tdim; ++i) { _M(n * ntangents + j, i, q, 0) = vvt_flat[i] * wts[q] * moment_values(0, q, n, 0); } } } } } } } std::array>, 4> xview = impl::to_mdspan(x); std::array>, 4> Mview = impl::to_mdspan(M); std::array>, 4> xbuffer; std::array>, 4> Mbuffer; if (discontinuous) { std::array>, 4> xshape; std::array>, 4> Mshape; std::tie(xbuffer, xshape, Mbuffer, Mshape) = element::make_discontinuous(xview, Mview, tdim, tdim * tdim); xview = impl::to_mdspan(xbuffer, xshape); Mview = impl::to_mdspan(Mbuffer, Mshape); } sobolev::space space = discontinuous ? sobolev::space::L2 : sobolev::space::HDivDiv; return FiniteElement( element::family::HHJ, celltype, polyset::type::standard, degree, {tdim, tdim}, impl::mdspan_t(wcoeffs.data(), wcoeffs.extents()), xview, Mview, 0, maps::type::doubleContravariantPiola, space, discontinuous, -1, degree, element::lagrange_variant::unset, element::dpc_variant::unset); } //----------------------------------------------------------------------------- template FiniteElement element::create_hhj(cell::type, int, bool); template FiniteElement element::create_hhj(cell::type, int, bool); //----------------------------------------------------------------------------- fenics-basix-0.9.0/cpp/basix/e-hhj.h000066400000000000000000000011611470517546000171370ustar00rootroot00000000000000// Copyright (c) 2022 Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #pragma once #include "cell.h" #include "finite-element.h" #include namespace basix::element { /// Create Hellan-Herrmann-Johnson element /// @param[in] celltype The cell type /// @param[in] degree The degree of the element /// @param[in] discontinuous Controls whether the element is continuous or /// discontinuous /// @return A finite element template FiniteElement create_hhj(cell::type celltype, int degree, bool discontinuous); } // namespace basix::element fenics-basix-0.9.0/cpp/basix/e-lagrange.cpp000066400000000000000000000726701470517546000205160ustar00rootroot00000000000000// Copyright (c) 2020-2022 Chris Richardson, Matthew Scroggs and Garth N. Wells // FEniCS Project // SPDX-License-Identifier: MIT #include "e-lagrange.h" #include "lattice.h" #include "maps.h" #include "math.h" #include "mdspan.hpp" #include "moments.h" #include "polynomials.h" #include "polyset.h" #include "quadrature.h" #include "sobolev-spaces.h" #include using namespace basix; namespace stdex = MDSPAN_IMPL_STANDARD_NAMESPACE::MDSPAN_IMPL_PROPOSED_NAMESPACE; namespace { //---------------------------------------------------------------------------- std::tuple variant_to_lattice(cell::type celltype, element::lagrange_variant variant) { switch (variant) { case element::lagrange_variant::equispaced: return {lattice::type::equispaced, lattice::simplex_method::none, true}; case element::lagrange_variant::gll_warped: return {lattice::type::gll, lattice::simplex_method::warp, true}; case element::lagrange_variant::gll_isaac: return {lattice::type::gll, lattice::simplex_method::isaac, true}; case element::lagrange_variant::gll_centroid: return {lattice::type::gll, lattice::simplex_method::centroid, true}; case element::lagrange_variant::chebyshev_warped: { if (celltype == cell::type::interval or celltype == cell::type::quadrilateral or celltype == cell::type::hexahedron) { return {lattice::type::chebyshev, lattice::simplex_method::none, false}; } else { // TODO: is this the best thing to do for simplices? return {lattice::type::chebyshev_plus_endpoints, lattice::simplex_method::warp, false}; } } case element::lagrange_variant::chebyshev_isaac: { if (celltype == cell::type::interval or celltype == cell::type::quadrilateral or celltype == cell::type::hexahedron) { return {lattice::type::chebyshev, lattice::simplex_method::none, false}; } else { // TODO: is this the best thing to do for simplices? return {lattice::type::chebyshev_plus_endpoints, lattice::simplex_method::isaac, false}; } } case element::lagrange_variant::chebyshev_centroid: return {lattice::type::chebyshev, lattice::simplex_method::centroid, false}; case element::lagrange_variant::gl_warped: { if (celltype == cell::type::interval or celltype == cell::type::quadrilateral or celltype == cell::type::hexahedron) { return {lattice::type::gl, lattice::simplex_method::none, false}; } else { // TODO: is this the best thing to do for simplices? return {lattice::type::gl_plus_endpoints, lattice::simplex_method::warp, false}; } } case element::lagrange_variant::gl_isaac: { if (celltype == cell::type::interval or celltype == cell::type::quadrilateral or celltype == cell::type::hexahedron) { return {lattice::type::gl, lattice::simplex_method::none, false}; } else { // TODO: is this the best thing to do for simplices? return {lattice::type::gl_plus_endpoints, lattice::simplex_method::isaac, false}; } } case element::lagrange_variant::gl_centroid: return {lattice::type::gl, lattice::simplex_method::centroid, false}; default: throw std::runtime_error("Unsupported variant"); } } //----------------------------------------------------------------------------- template FiniteElement create_d_lagrange(cell::type celltype, int degree, element::lagrange_variant variant, lattice::type lattice_type, lattice::simplex_method simplex_method) { if (celltype == cell::type::prism or celltype == cell::type::pyramid) { throw std::runtime_error( "This variant is not yet supported on prisms and pyramids."); } const std::size_t tdim = cell::topological_dimension(celltype); const std::size_t ndofs = polyset::dim(celltype, polyset::type::standard, degree); const std::vector>> topology = cell::topology(celltype); std::array>, 4> x; std::array>, 4> M; for (std::size_t i = 0; i < tdim; ++i) { std::size_t num_ent = cell::num_sub_entities(celltype, i); x[i] = std::vector>(num_ent, impl::mdarray_t(0, tdim)); M[i] = std::vector>( num_ent, impl::mdarray_t(0, 1, 0, 1)); } const int lattice_degree = celltype == cell::type::triangle ? degree + 3 : (celltype == cell::type::tetrahedron ? degree + 4 : degree + 2); // Create points in interior const auto [pt, shape] = lattice::create( celltype, lattice_degree, lattice_type, false, simplex_method); x[tdim].emplace_back(shape, pt); const std::size_t num_dofs = shape[0]; auto& _M = M[tdim].emplace_back(num_dofs, 1, num_dofs, 1); for (std::size_t i = 0; i < _M.extent(0); ++i) _M(i, 0, i, 0) = 1.0; return FiniteElement( element::family::P, celltype, polyset::type::standard, degree, {}, impl::mdspan_t(math::eye(ndofs).data(), ndofs, ndofs), impl::to_mdspan(x), impl::to_mdspan(M), 0, maps::type::identity, sobolev::space::L2, true, degree, degree, variant, element::dpc_variant::unset); } //---------------------------------------------------------------------------- template FiniteElement create_d_iso(cell::type celltype, int degree, element::lagrange_variant variant, lattice::type lattice_type, lattice::simplex_method simplex_method) { if (celltype == cell::type::prism or celltype == cell::type::pyramid) { throw std::runtime_error( "This variant is not yet supported on prisms and pyramids."); } const std::size_t tdim = cell::topological_dimension(celltype); const std::size_t ndofs = polyset::dim(celltype, polyset::type::macroedge, degree); const std::vector>> topology = cell::topology(celltype); std::array>, 4> x; std::array>, 4> M; for (std::size_t i = 0; i < tdim; ++i) { std::size_t num_ent = cell::num_sub_entities(celltype, i); x[i] = std::vector>(num_ent, impl::mdarray_t(0, tdim)); M[i] = std::vector>( num_ent, impl::mdarray_t(0, 1, 0, 1)); } const int lattice_degree = celltype == cell::type::triangle ? 2 * degree + 3 : (celltype == cell::type::tetrahedron ? 2 * degree + 4 : 2 * degree + 2); // Create points in interior const auto [pt, shape] = lattice::create( celltype, lattice_degree, lattice_type, false, simplex_method); x[tdim].emplace_back(shape, pt); const std::size_t num_dofs = shape[0]; auto& _M = M[tdim].emplace_back(num_dofs, 1, num_dofs, 1); for (std::size_t i = 0; i < _M.extent(0); ++i) _M(i, 0, i, 0) = 1.0; return FiniteElement( element::family::iso, celltype, polyset::type::macroedge, degree, {}, impl::mdspan_t(math::eye(ndofs).data(), ndofs, ndofs), impl::to_mdspan(x), impl::to_mdspan(M), 0, maps::type::identity, sobolev::space::L2, true, degree, degree, variant, element::dpc_variant::unset); } //---------------------------------------------------------------------------- template FiniteElement create_legendre(cell::type celltype, int degree, bool discontinuous) { if (!discontinuous) throw std::runtime_error("Legendre variant must be discontinuous"); const std::size_t tdim = cell::topological_dimension(celltype); const std::size_t ndofs = polyset::dim(celltype, polyset::type::standard, degree); const std::vector>> topology = cell::topology(celltype); std::array>, 4> x; std::array>, 4> M; // Evaluate moment space at quadrature points const auto [_pts, wts] = quadrature::make_quadrature( quadrature::type::Default, celltype, polyset::type::standard, degree * 2); assert(!wts.empty()); impl::mdspan_t pts(_pts.data(), wts.size(), _pts.size() / wts.size()); const auto [_phi, pshape] = polynomials::tabulate(polynomials::type::legendre, celltype, degree, pts); impl::mdspan_t phi(_phi.data(), pshape); for (std::size_t d = 0; d < tdim; ++d) { for (std::size_t e = 0; e < topology[d].size(); ++e) { x[d].emplace_back(0, tdim); M[d].emplace_back(0, 1, 0, 1); } } auto& _x = x[tdim].emplace_back(pts.extents()); std::copy_n(pts.data_handle(), pts.size(), _x.data()); auto& _M = M[tdim].emplace_back(ndofs, 1, pts.extent(0), 1); for (std::size_t i = 0; i < ndofs; ++i) for (std::size_t j = 0; j < pts.extent(0); ++j) _M(i, 0, j, 0) = phi(i, j) * wts[j]; sobolev::space space = discontinuous ? sobolev::space::L2 : sobolev::space::H1; return FiniteElement( element::family::P, celltype, polyset::type::standard, degree, {}, impl::mdspan_t(math::eye(ndofs).data(), ndofs, ndofs), impl::to_mdspan(x), impl::to_mdspan(M), 0, maps::type::identity, space, discontinuous, degree, degree, element::lagrange_variant::legendre, element::dpc_variant::unset); } //----------------------------------------------------------------------------- template FiniteElement create_bernstein(cell::type celltype, int degree, bool discontinuous) { assert(degree > 0); if (celltype != cell::type::interval and celltype != cell::type::triangle and celltype != cell::type::tetrahedron) { throw std::runtime_error( "Bernstein elements are currently only supported on simplices."); } const std::size_t tdim = cell::topological_dimension(celltype); const std::vector>> topology = cell::topology(celltype); std::array>, 4> x; std::array>, 4> M; const std::array nb = {1, static_cast(polynomials::dim( polynomials::type::bernstein, cell::type::interval, degree)), static_cast(polynomials::dim( polynomials::type::bernstein, cell::type::triangle, degree)), static_cast(polynomials::dim( polynomials::type::bernstein, cell::type::tetrahedron, degree))}; constexpr std::array ct = {cell::type::point, cell::type::interval, cell::type::triangle, cell::type::tetrahedron}; const std::array nb_interior = {1, degree < 2 ? 0 : nb[1] - 2, degree < 3 ? 0 : nb[2] + 3 - 3 * nb[1], degree < 4 ? 0 : nb[3] + 6 * nb[1] - 4 * nb[2] - 4}; std::array, 4> bernstein_bubbles; bernstein_bubbles[0].push_back(0); { // scope int ib = 0; for (int i = 0; i <= degree; ++i) { if (i > 0 and i < degree) { bernstein_bubbles[1].push_back(ib); } ++ib; } } { // scope int ib = 0; for (int i = 0; i <= degree; ++i) { for (int j = 0; j <= degree - i; ++j) { if (i > 0 and j > 0 and i + j < degree) bernstein_bubbles[2].push_back(ib); ++ib; } } } { // scope int ib = 0; for (int i = 0; i <= degree; ++i) { for (int j = 0; j <= degree - i; ++j) { for (int k = 0; k <= degree - i - j; ++k) { if (i > 0 and j > 0 and k > 0 and i + j + k < degree) bernstein_bubbles[3].push_back(ib); ++ib; } } } } for (std::size_t v = 0; v < topology[0].size(); ++v) { const auto [entity, shape] = cell::sub_entity_geometry(celltype, 0, v); x[0].emplace_back(shape, entity); M[0].emplace_back(std::array{1, 1, 1, 1}, 1); } for (std::size_t d = 1; d <= tdim; ++d) { if (nb_interior[d] == 0) { for (std::size_t e = 0; e < topology[d].size(); ++e) { x[d].emplace_back(0, tdim); M[d].emplace_back(0, 1, 0, 1); } } else { const auto [_pts, wts] = quadrature::make_quadrature(quadrature::type::Default, ct[d], polyset::type::standard, degree * 2); assert(!wts.empty()); impl::mdspan_t pts(_pts.data(), wts.size(), _pts.size() / wts.size()); const auto [_phi, pshape] = polynomials::tabulate( polynomials::type::legendre, ct[d], degree, pts); impl::mdspan_t phi(_phi.data(), pshape); const auto [_bern, bshape] = polynomials::tabulate( polynomials::type::bernstein, ct[d], degree, pts); impl::mdspan_t bern(_bern.data(), bshape); assert(phi.extent(0) == nb[d]); const std::size_t npts = pts.extent(0); impl::mdarray_t mat(nb[d], nb[d]); for (std::size_t i = 0; i < nb[d]; ++i) for (std::size_t j = 0; j < nb[d]; ++j) for (std::size_t k = 0; k < wts.size(); ++k) mat(i, j) += wts[k] * bern(j, k) * phi(i, k); impl::mdarray_t minv(mat.extents()); { std::vector id = math::eye(nb[d]); impl::mdspan_t _id(id.data(), nb[d], nb[d]); impl::mdspan_t _mat(mat.data(), mat.extents()); std::vector minv_data = math::solve(_mat, _id); std::ranges::copy(minv_data, minv.data()); } M[d] = std::vector>( cell::num_sub_entities(celltype, d), impl::mdarray_t(nb_interior[d], 1, npts, 1)); for (std::size_t e = 0; e < topology[d].size(); ++e) { auto [_entity_x, shape] = cell::sub_entity_geometry(celltype, d, e); impl::mdspan_t entity_x(_entity_x.data(), shape); std::span x0(entity_x.data_handle(), shape[1]); { auto& _x = x[d].emplace_back(pts.extent(0), shape[1]); for (std::size_t i = 0; i < _x.extent(0); ++i) for (std::size_t j = 0; j < _x.extent(1); ++j) _x(i, j) = x0[j]; } for (std::size_t j = 0; j < pts.extent(0); ++j) for (std::size_t k0 = 0; k0 < pts.extent(1); ++k0) for (std::size_t k1 = 0; k1 < shape[1]; ++k1) x[d][e](j, k1) += (entity_x(k0 + 1, k1) - x0[k1]) * pts(j, k0); for (std::size_t i = 0; i < bernstein_bubbles[d].size(); ++i) { for (std::size_t p = 0; p < npts; ++p) { T tmp = 0.0; for (std::size_t k = 0; k < phi.extent(0); ++k) tmp += phi(k, p) * minv(bernstein_bubbles[d][i], k); M[d][e](i, 0, p, 0) = wts[p] * tmp; } } } } } sobolev::space space = discontinuous ? sobolev::space::L2 : sobolev::space::H1; const std::size_t ndofs = polyset::dim(celltype, polyset::type::standard, degree); return FiniteElement( element::family::P, celltype, polyset::type::standard, degree, {}, impl::mdspan_t(math::eye(ndofs).data(), ndofs, ndofs), impl::to_mdspan(x), impl::to_mdspan(M), 0, maps::type::identity, space, discontinuous, degree, degree, element::lagrange_variant::bernstein, element::dpc_variant::unset); } //----------------------------------------------------------------------------- } // namespace //---------------------------------------------------------------------------- template FiniteElement basix::element::create_lagrange(cell::type celltype, int degree, lagrange_variant variant, bool discontinuous, std::vector dof_ordering) { if (celltype == cell::type::point) { if (degree != 0) throw std::runtime_error("Can only create order 0 Lagrange on a point"); std::array>, 4> x; std::array>, 4> M; x[0].emplace_back(1, 0); M[0].emplace_back( MDSPAN_IMPL_STANDARD_NAMESPACE::dextents{1, 1, 1, 1}, 1); return FiniteElement( family::P, cell::type::point, polyset::type::standard, 0, {}, impl::mdspan_t(math::eye(1).data(), 1, 1), impl::to_mdspan(x), impl::to_mdspan(M), 0, maps::type::identity, sobolev::space::H1, discontinuous, degree, degree, element::lagrange_variant::unset, element::dpc_variant::unset, dof_ordering); } if (variant == lagrange_variant::legendre) return create_legendre(celltype, degree, discontinuous); if (variant == element::lagrange_variant::bernstein) { if (degree == 0) variant = lagrange_variant::unset; else return create_bernstein(celltype, degree, discontinuous); } if (variant == lagrange_variant::unset) { if (degree < 3) variant = element::lagrange_variant::gll_warped; else { throw std::runtime_error( "Lagrange elements of degree > 2 need to be given a variant."); } } auto [lattice_type, simplex_method, exterior] = variant_to_lattice(celltype, variant); if (!exterior) { // Points used to define this variant are all interior to the cell, // so this variant requires that the element is discontinuous if (!discontinuous) { throw std::runtime_error("This variant of Lagrange is only supported for " "discontinuous elements"); } return create_d_lagrange(celltype, degree, variant, lattice_type, simplex_method); } const std::size_t tdim = cell::topological_dimension(celltype); const std::size_t ndofs = polyset::dim(celltype, polyset::type::standard, degree); const std::vector>> topology = cell::topology(celltype); std::array>, 4> x; std::array>, 4> M; if (degree == 0) { if (!discontinuous) { throw std::runtime_error( "Cannot create a continuous order 0 Lagrange basis function"); } for (std::size_t i = 0; i < tdim; ++i) { std::size_t num_entities = cell::num_sub_entities(celltype, i); x[i] = std::vector(num_entities, impl::mdarray_t(0, tdim)); M[i] = std::vector(num_entities, impl::mdarray_t(0, 1, 0, 1)); } const auto [pt, shape] = lattice::create(celltype, 0, lattice_type, true, simplex_method); x[tdim].emplace_back( MDSPAN_IMPL_STANDARD_NAMESPACE::dextents{shape[0], shape[1]}, pt); auto& _M = M[tdim].emplace_back(shape[0], 1, shape[0], 1); std::fill(_M.data(), _M.data() + _M.size(), 0); for (std::size_t i = 0; i < shape[0]; ++i) _M(i, 0, i, 0) = 1; } else { // Create points at nodes, ordered by topology (vertices first) for (std::size_t dim = 0; dim <= tdim; ++dim) { // Loop over entities of dimension 'dim' for (std::size_t e = 0; e < topology[dim].size(); ++e) { const auto [entity_x, entity_x_shape] = cell::sub_entity_geometry(celltype, dim, e); if (dim == 0) { x[dim].emplace_back( MDSPAN_IMPL_STANDARD_NAMESPACE::dextents{ entity_x_shape[0], entity_x_shape[1]}, entity_x); auto& _M = M[dim].emplace_back(entity_x_shape[0], 1, entity_x_shape[0], 1); std::fill(_M.data(), _M.data() + _M.size(), 0); for (std::size_t i = 0; i < entity_x_shape[0]; ++i) _M(i, 0, i, 0) = 1; } else if (dim == tdim) { const auto [pt, shape] = lattice::create( celltype, degree, lattice_type, false, simplex_method); x[dim].emplace_back( MDSPAN_IMPL_STANDARD_NAMESPACE::dextents{ shape[0], shape[1]}, pt); auto& _M = M[dim].emplace_back(shape[0], 1, shape[0], 1); std::fill(_M.data(), _M.data() + _M.size(), 0); for (std::size_t i = 0; i < shape[0]; ++i) _M(i, 0, i, 0) = 1; } else { cell::type ct = cell::sub_entity_type(celltype, dim, e); const auto [pt, shape] = lattice::create(ct, degree, lattice_type, false, simplex_method); impl::mdspan_t lattice(pt.data(), shape); std::span x0(entity_x.data(), entity_x_shape[1]); impl::mdspan_t entity_x_view(entity_x.data(), entity_x_shape); auto& _x = x[dim].emplace_back(shape[0], entity_x_shape[1]); for (std::size_t i = 0; i < shape[0]; ++i) for (std::size_t j = 0; j < entity_x_shape[1]; ++j) _x(i, j) = x0[j]; for (std::size_t j = 0; j < shape[0]; ++j) for (std::size_t k = 0; k < shape[1]; ++k) for (std::size_t q = 0; q < tdim; ++q) _x(j, q) += (entity_x_view(k + 1, q) - x0[q]) * lattice(j, k); auto& _M = M[dim].emplace_back(shape[0], 1, shape[0], 1); std::fill(_M.data(), _M.data() + _M.size(), 0); for (std::size_t i = 0; i < shape[0]; ++i) _M(i, 0, i, 0) = 1; } } } } std::array>, 4> xview = impl::to_mdspan(x); std::array>, 4> Mview = impl::to_mdspan(M); std::array>, 4> xbuffer; std::array>, 4> Mbuffer; if (discontinuous) { std::array>, 4> xshape; std::array>, 4> Mshape; std::tie(xbuffer, xshape, Mbuffer, Mshape) = make_discontinuous(xview, Mview, tdim, 1); xview = impl::to_mdspan(xbuffer, xshape); Mview = impl::to_mdspan(Mbuffer, Mshape); } sobolev::space space = discontinuous ? sobolev::space::L2 : sobolev::space::H1; return FiniteElement( family::P, celltype, polyset::type::standard, degree, {}, impl::mdspan_t(math::eye(ndofs).data(), ndofs, ndofs), xview, Mview, 0, maps::type::identity, space, discontinuous, degree, degree, variant, dpc_variant::unset, dof_ordering); } //----------------------------------------------------------------------------- template FiniteElement basix::element::create_iso(cell::type celltype, int degree, lagrange_variant variant, bool discontinuous) { if (celltype != cell::type::interval && celltype != cell::type::quadrilateral && celltype != cell::type::hexahedron && celltype != cell::type::triangle && celltype != cell::type::tetrahedron) { throw std::runtime_error( "Can currently only create iso elements on " "intervals, triangles, tetrahedra, quadrilaterals, and hexahedra"); } if (variant == lagrange_variant::unset) { if (degree < 3) variant = element::lagrange_variant::gll_warped; else { throw std::runtime_error( "Lagrange elements of degree > 2 need to be given a variant."); } } auto [lattice_type, simplex_method, exterior] = variant_to_lattice(celltype, variant); if (!exterior) { // Points used to define this variant are all interior to the cell, // so this variant requires that the element is discontinuous if (!discontinuous) { throw std::runtime_error("This variant of Lagrange is only supported for " "discontinuous elements"); } return create_d_iso(celltype, degree, variant, lattice_type, simplex_method); } const std::size_t tdim = cell::topological_dimension(celltype); const std::size_t ndofs = polyset::dim(celltype, polyset::type::macroedge, degree); const std::vector>> topology = cell::topology(celltype); std::array>, 4> x; std::array>, 4> M; if (degree == 0) { if (!discontinuous) { throw std::runtime_error( "Cannot create a continuous order 0 Lagrange basis function"); } for (std::size_t i = 0; i < tdim; ++i) { std::size_t num_entities = cell::num_sub_entities(celltype, i); x[i] = std::vector(num_entities, impl::mdarray_t(0, tdim)); M[i] = std::vector(num_entities, impl::mdarray_t(0, 1, 0, 1)); } const auto [pt, shape] = lattice::create(celltype, 0, lattice_type, true, simplex_method); x[tdim].emplace_back(std::array{shape[0], shape[1]}, pt); auto& _M = M[tdim].emplace_back( std::array{shape[0], 1, shape[0], 1}, 0); for (std::size_t i = 0; i < shape[0]; ++i) _M(i, 0, i, 0) = 1; } else { // Create points at nodes, ordered by topology (vertices first) for (std::size_t dim = 0; dim <= tdim; ++dim) { // Loop over entities of dimension 'dim' for (std::size_t e = 0; e < topology[dim].size(); ++e) { const auto [entity_x, entity_x_shape] = cell::sub_entity_geometry(celltype, dim, e); if (dim == 0) { x[dim].emplace_back(entity_x_shape, entity_x); auto& _M = M[dim].emplace_back( std::array{entity_x_shape[0], 1, entity_x_shape[0], 1}, 0); for (std::size_t i = 0; i < entity_x_shape[0]; ++i) _M(i, 0, i, 0) = 1; } else if (dim == tdim) { const auto [pt, shape] = lattice::create( celltype, 2 * degree, lattice_type, false, simplex_method); x[dim].emplace_back(shape, pt); auto& _M = M[dim].emplace_back( std::array{shape[0], 1, shape[0], 1}, 0); for (std::size_t i = 0; i < shape[0]; ++i) _M(i, 0, i, 0) = 1; } else { cell::type ct = cell::sub_entity_type(celltype, dim, e); const auto [pt, shape] = lattice::create( ct, 2 * degree, lattice_type, false, simplex_method); impl::mdspan_t lattice(pt.data(), shape); std::span x0(entity_x.data(), entity_x_shape[1]); impl::mdspan_t entity_x_view(entity_x.data(), entity_x_shape); auto& _x = x[dim].emplace_back(shape[0], entity_x_shape[1]); for (std::size_t i = 0; i < shape[0]; ++i) for (std::size_t j = 0; j < entity_x_shape[1]; ++j) _x(i, j) = x0[j]; for (std::size_t j = 0; j < shape[0]; ++j) for (std::size_t k = 0; k < shape[1]; ++k) for (std::size_t q = 0; q < tdim; ++q) _x(j, q) += (entity_x_view(k + 1, q) - x0[q]) * lattice(j, k); auto& _M = M[dim].emplace_back(shape[0], 1, shape[0], 1); std::fill(_M.data(), _M.data() + _M.size(), 0); for (std::size_t i = 0; i < shape[0]; ++i) _M(i, 0, i, 0) = 1; } } } } std::array>, 4> xview = impl::to_mdspan(x); std::array>, 4> Mview = impl::to_mdspan(M); std::array>, 4> xbuffer; std::array>, 4> Mbuffer; if (discontinuous) { std::array>, 4> xshape; std::array>, 4> Mshape; std::tie(xbuffer, xshape, Mbuffer, Mshape) = make_discontinuous(xview, Mview, tdim, 1); xview = impl::to_mdspan(xbuffer, xshape); Mview = impl::to_mdspan(Mbuffer, Mshape); } sobolev::space space = discontinuous ? sobolev::space::L2 : sobolev::space::H1; return FiniteElement( family::iso, celltype, polyset::type::macroedge, degree, {}, impl::mdspan_t(math::eye(ndofs).data(), ndofs, ndofs), xview, Mview, 0, maps::type::identity, space, discontinuous, degree, degree, variant, dpc_variant::unset); } //----------------------------------------------------------------------------- template FiniteElement element::create_lagrange(cell::type, int, lagrange_variant, bool, std::vector); template FiniteElement element::create_lagrange(cell::type, int, lagrange_variant, bool, std::vector); template FiniteElement element::create_iso(cell::type, int, lagrange_variant, bool); template FiniteElement element::create_iso(cell::type, int, lagrange_variant, bool); //----------------------------------------------------------------------------- fenics-basix-0.9.0/cpp/basix/e-lagrange.h000066400000000000000000000025541470517546000201550ustar00rootroot00000000000000// Copyright (c) 2020-2022 Chris Richardson, Matthew Scroggs and Garth N. Wells // FEniCS Project // SPDX-License-Identifier: MIT #pragma once #include "cell.h" #include "element-families.h" #include "finite-element.h" #include namespace basix::element { /// @brief Create a Lagrange(-like) element on cell with given degree /// @param[in] celltype The element cell type /// @param[in] degree The degree of the element /// @param[in] variant The variant of the element to be created /// @param[in] discontinuous True if the is discontinuous /// @param[in] dof_ordering DOF reordering /// @return A finite element template FiniteElement create_lagrange(cell::type celltype, int degree, lagrange_variant variant, bool discontinuous, std::vector dof_ordering = {}); /// @brief Create an iso macro element on cell with given degree /// @param[in] celltype The element cell type /// @param[in] degree The degree of the element /// @param[in] variant The variant of the element to be created /// @param[in] discontinuous True if the is discontinuous /// @return A finite element template FiniteElement create_iso(cell::type celltype, int degree, lagrange_variant variant, bool discontinuous); } // namespace basix::element fenics-basix-0.9.0/cpp/basix/e-nce-rtc.cpp000066400000000000000000000304471470517546000202650ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson & Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #include "e-nce-rtc.h" #include "e-lagrange.h" #include "element-families.h" #include "maps.h" #include "moments.h" #include "polyset.h" #include "quadrature.h" #include "sobolev-spaces.h" #include #include using namespace basix; //---------------------------------------------------------------------------- template FiniteElement basix::element::create_rtc(cell::type celltype, int degree, element::lagrange_variant lvariant, bool discontinuous) { if (celltype != cell::type::quadrilateral and celltype != cell::type::hexahedron) { throw std::runtime_error("Unsupported cell type"); } if (degree < 1) throw std::runtime_error("Degree must be at least 1"); const std::size_t tdim = cell::topological_dimension(celltype); const cell::type facettype = (tdim == 2) ? cell::type::interval : cell::type::quadrilateral; // Evaluate the expansion polynomials at the quadrature points const auto [_pts, qwts] = quadrature::make_quadrature( quadrature::type::Default, celltype, polyset::type::standard, 2 * degree); impl::mdspan_t pts(_pts.data(), qwts.size(), _pts.size() / qwts.size()); const auto [_phi, shape] = polyset::tabulate(celltype, polyset::type::standard, degree, 0, pts); impl::mdspan_t phi(_phi.data(), shape); // The number of order (degree) polynomials const std::size_t psize = phi.extent(1); const int facet_count = tdim == 2 ? 4 : 6; const int facet_dofs = polyset::dim(facettype, polyset::type::standard, degree - 1); const int internal_dofs = tdim == 2 ? 2 * degree * (degree - 1) : 3 * degree * degree * (degree - 1); const std::size_t ndofs = facet_count * facet_dofs + internal_dofs; // Create coefficients for order (degree-1) vector polynomials impl::mdarray_t wcoeffs(ndofs, psize * tdim); const int nv = polyset::dim(cell::type::interval, polyset::type::standard, degree); const int ns = polyset::dim(cell::type::interval, polyset::type::standard, degree - 1); int dof = 0; if (tdim == 2) { for (int i = 0; i < ns; ++i) { for (int j = 0; j < nv; ++j) { wcoeffs(dof++, j * nv + i) = 1; wcoeffs(dof++, psize + i * nv + j) = 1; } } } else { for (int i = 0; i < ns; ++i) { for (int j = 0; j < ns; ++j) { for (int k = 0; k < nv; ++k) { wcoeffs(dof++, k * nv * nv + j * nv + i) = 1; wcoeffs(dof++, psize + i * nv * nv + k * nv + j) = 1; wcoeffs(dof++, psize * 2 + j * nv * nv + i * nv + k) = 1; } } } } assert((std::size_t)dof == ndofs); std::array>, 4> x; std::array>, 4> M; for (std::size_t i = 0; i < tdim - 1; ++i) { const std::size_t num_ent = cell::num_sub_entities(celltype, i); x[i] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[i] = std::vector(num_ent, impl::mdarray_t(0, tdim, 0, 1)); } { FiniteElement moment_space = element::create_lagrange(facettype, degree - 1, lvariant, true); auto [_x, xshape, _M, Mshape] = moments::make_normal_integral_moments( moment_space, celltype, polyset::type::standard, tdim, 2 * degree - 1); assert(_x.size() == _M.size()); for (std::size_t i = 0; i < _x.size(); ++i) { x[tdim - 1].emplace_back(std::array{xshape[0], xshape[1]}, _x[i]); M[tdim - 1].emplace_back( std::array{Mshape[0], Mshape[1], Mshape[2], Mshape[3]}, _M[i]); } } // Add integral moments on interior if (degree > 1) { auto [_x, xshape, _M, Mshape] = moments::make_dot_integral_moments( element::create_nce(celltype, degree - 1, lvariant, true), celltype, polyset::type::standard, tdim, 2 * degree - 1); assert(_x.size() == _M.size()); for (std::size_t i = 0; i < _x.size(); ++i) { x[tdim].emplace_back(std::array{xshape[0], xshape[1]}, _x[i]); M[tdim].emplace_back( std::array{Mshape[0], Mshape[1], Mshape[2], Mshape[3]}, _M[i]); } } else { const std::size_t num_ent = cell::num_sub_entities(celltype, tdim); x[tdim] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[tdim] = std::vector(num_ent, impl::mdarray_t(0, tdim, 0, 1)); } std::array>, 4> xview = impl::to_mdspan(x); std::array>, 4> Mview = impl::to_mdspan(M); std::array>, 4> xbuffer; std::array>, 4> Mbuffer; if (discontinuous) { std::array>, 4> xshape; std::array>, 4> Mshape; std::tie(xbuffer, xshape, Mbuffer, Mshape) = element::make_discontinuous(xview, Mview, tdim, tdim); xview = impl::to_mdspan(xbuffer, xshape); Mview = impl::to_mdspan(Mbuffer, Mshape); } sobolev::space space = discontinuous ? sobolev::space::L2 : sobolev::space::HCurl; return FiniteElement( element::family::RT, celltype, polyset::type::standard, degree, {tdim}, impl::mdspan_t(wcoeffs.data(), wcoeffs.extents()), xview, Mview, 0, maps::type::contravariantPiola, space, discontinuous, degree - 1, degree, lvariant, element::dpc_variant::unset); } //----------------------------------------------------------------------------- template FiniteElement basix::element::create_nce(cell::type celltype, int degree, element::lagrange_variant lvariant, bool discontinuous) { if (celltype != cell::type::quadrilateral and celltype != cell::type::hexahedron) { throw std::runtime_error("Unsupported cell type"); } if (degree < 1) throw std::runtime_error("Degree must be at least 1"); const std::size_t tdim = cell::topological_dimension(celltype); // Evaluate the expansion polynomials at the quadrature points const auto [_pts, wts] = quadrature::make_quadrature( quadrature::type::Default, celltype, polyset::type::standard, 2 * degree); impl::mdspan_t pts(_pts.data(), wts.size(), _pts.size() / wts.size()); const auto [_phi, shape] = polyset::tabulate(celltype, polyset::type::standard, degree, 0, pts); impl::mdspan_t phi(_phi.data(), shape); // The number of order (degree) polynomials const int psize = phi.extent(1); const int edge_count = tdim == 2 ? 4 : 12; const int edge_dofs = polyset::dim(cell::type::interval, polyset::type::standard, degree - 1); const int face_count = tdim == 2 ? 1 : 6; const int face_dofs = 2 * degree * (degree - 1); const int volume_count = tdim == 2 ? 0 : 1; const int volume_dofs = 3 * degree * (degree - 1) * (degree - 1); const std::size_t ndofs = edge_count * edge_dofs + face_count * face_dofs + volume_count * volume_dofs; // Create coefficients for order (degree-1) vector polynomials impl::mdarray_t wcoeffs(ndofs, psize * tdim); const int nv = polyset::dim(cell::type::interval, polyset::type::standard, degree); const int ns = polyset::dim(cell::type::interval, polyset::type::standard, degree - 1); int dof = 0; if (tdim == 2) { for (int i = 0; i < ns; ++i) { for (int j = 0; j < nv; ++j) { wcoeffs(dof++, i * nv + j) = 1; wcoeffs(dof++, psize + j * nv + i) = 1; } } } else { for (int i = 0; i < ns; ++i) { for (int j = 0; j < nv; ++j) { for (int k = 0; k < nv; ++k) { wcoeffs(dof++, i * nv * nv + j * nv + k) = 1; wcoeffs(dof++, psize + k * nv * nv + i * nv + j) = 1; wcoeffs(dof++, psize * 2 + j * nv * nv + k * nv + i) = 1; } } } } assert((std::size_t)dof == ndofs); std::array>, 4> x; std::array>, 4> M; x[0] = std::vector(cell::num_sub_entities(celltype, 0), impl::mdarray_t(0, tdim)); M[0] = std::vector(cell::num_sub_entities(celltype, 0), impl::mdarray_t(0, tdim, 0, 1)); { FiniteElement edge_moment_space = element::create_lagrange( cell::type::interval, degree - 1, lvariant, true); auto [_x, xshape, _M, Mshape] = moments::make_tangent_integral_moments( edge_moment_space, celltype, polyset::type::standard, tdim, 2 * degree - 1); assert(_x.size() == _M.size()); for (std::size_t i = 0; i < _x.size(); ++i) { x[1].emplace_back(std::array{xshape[0], xshape[1]}, _x[i]); M[1].emplace_back(std::array{Mshape[0], Mshape[1], Mshape[2], Mshape[3]}, _M[i]); } } // Add integral moments on interior if (degree > 1) { // Face integral moment FiniteElement moment_space = element::create_rtc( cell::type::quadrilateral, degree - 1, lvariant, true); auto [_x, xshape, _M, Mshape] = moments::make_dot_integral_moments( moment_space, celltype, polyset::type::standard, tdim, 2 * degree - 1); assert(_x.size() == _M.size()); for (std::size_t i = 0; i < _x.size(); ++i) { x[2].emplace_back(std::array{xshape[0], xshape[1]}, _x[i]); M[2].emplace_back(std::array{Mshape[0], Mshape[1], Mshape[2], Mshape[3]}, _M[i]); } } else { const std::size_t num_ent = cell::num_sub_entities(celltype, 2); x[2] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[2] = std::vector(num_ent, impl::mdarray_t(0, tdim, 0, 1)); } if (tdim == 3) { if (degree > 1) { FiniteElement moment_space = element::create_rtc( cell::type::hexahedron, degree - 1, lvariant, true); auto [_x, xshape, _M, Mshape] = moments::make_dot_integral_moments( moment_space, celltype, polyset::type::standard, tdim, 2 * degree - 1); assert(_x.size() == _M.size()); for (std::size_t i = 0; i < _x.size(); ++i) { x[3].emplace_back(std::array{xshape[0], xshape[1]}, _x[i]); M[3].emplace_back( std::array{Mshape[0], Mshape[1], Mshape[2], Mshape[3]}, _M[i]); } } else { const std::size_t num_ent = cell::num_sub_entities(celltype, 3); x[3] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[3] = std::vector(num_ent, impl::mdarray_t(0, tdim, 0, 1)); } } std::array>, 4> xview = impl::to_mdspan(x); std::array>, 4> Mview = impl::to_mdspan(M); std::array>, 4> xbuffer; std::array>, 4> Mbuffer; if (discontinuous) { std::array>, 4> xshape; std::array>, 4> Mshape; std::tie(xbuffer, xshape, Mbuffer, Mshape) = element::make_discontinuous(xview, Mview, tdim, tdim); xview = impl::to_mdspan(xbuffer, xshape); Mview = impl::to_mdspan(Mbuffer, Mshape); } sobolev::space space = discontinuous ? sobolev::space::L2 : sobolev::space::HCurl; return FiniteElement( element::family::N1E, celltype, polyset::type::standard, degree, {tdim}, impl::mdspan_t(wcoeffs.data(), wcoeffs.extents()), xview, Mview, 0, maps::type::covariantPiola, space, discontinuous, degree - 1, degree, lvariant, element::dpc_variant::unset); } //----------------------------------------------------------------------------- template FiniteElement basix::element::create_rtc(cell::type, int, element::lagrange_variant, bool); template FiniteElement basix::element::create_rtc(cell::type, int, element::lagrange_variant, bool); template FiniteElement basix::element::create_nce(cell::type, int, element::lagrange_variant, bool); template FiniteElement basix::element::create_nce(cell::type, int, element::lagrange_variant, bool); //----------------------------------------------------------------------------- fenics-basix-0.9.0/cpp/basix/e-nce-rtc.h000066400000000000000000000025331470517546000177250ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson & Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #pragma once #include "cell.h" #include "finite-element.h" #include namespace basix::element { /// Create RTC H(div) element /// @param[in] celltype The cell type /// @param[in] degree The degree of the element /// @param[in] lvariant The Lagrange variant to use when defining the element to /// take integral moments against /// @param[in] discontinuous Controls whether the element is continuous or /// discontinuous /// @return A finite element template FiniteElement create_rtc(cell::type celltype, int degree, element::lagrange_variant lvariant, bool discontinuous); /// Create NC H(curl) element /// @param[in] celltype The cell type /// @param[in] degree The degree of the element /// @param[in] lvariant The Lagrange variant to use when defining the element to /// take integral moments against /// @param[in] discontinuous Controls whether the element is continuous or /// discontinuous /// @return A finite element template FiniteElement create_nce(cell::type celltype, int degree, element::lagrange_variant lvariant, bool discontinuous); } // namespace basix::element fenics-basix-0.9.0/cpp/basix/e-nedelec.cpp000066400000000000000000000360601470517546000203260ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson & Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #include "e-nedelec.h" #include "e-lagrange.h" #include "e-raviart-thomas.h" #include "element-families.h" #include "maps.h" #include "math.h" #include "mdspan.hpp" #include "moments.h" #include "polyset.h" #include "quadrature.h" #include "sobolev-spaces.h" #include #include #include using namespace basix; namespace { //----------------------------------------------------------------------------- template impl::mdarray_t create_nedelec_2d_space(int degree) { // Number of order (degree) vector polynomials const std::size_t nv = degree * (degree + 1) / 2; // Number of order (degree-1) vector polynomials const std::size_t ns0 = (degree - 1) * degree / 2; // Number of additional polynomials in Nedelec set const std::size_t ns = degree; // Tabulate polynomial set at quadrature points const auto [_pts, wts] = quadrature::make_quadrature( quadrature::type::Default, cell::type::triangle, polyset::type::standard, 2 * degree); impl::mdspan_t pts(_pts.data(), wts.size(), _pts.size() / wts.size()); const auto [_phi, shape] = polyset::tabulate( cell::type::triangle, polyset::type::standard, degree, 0, pts); impl::mdspan_t phi(_phi.data(), shape); const std::size_t psize = phi.extent(1); // Create coefficients for order (degree-1) vector polynomials impl::mdarray_t wcoeffs(nv * 2 + ns, psize * 2); for (std::size_t i = 0; i < nv; ++i) { wcoeffs(i, i) = 1.0; wcoeffs(nv + i, psize + i) = 1.0; } // Create coefficients for the additional Nedelec polynomials for (std::size_t i = 0; i < ns; ++i) { for (std::size_t j = nv; j < psize; ++j) { wcoeffs(2 * nv + i, j) = 0.0; wcoeffs(2 * nv + i, j + psize) = 0.0; for (std::size_t k = 0; k < wts.size(); ++k) { T p = phi(0, ns0 + i, k); wcoeffs(2 * nv + i, j) += wts[k] * p * pts(k, 1) * phi(0, j, k); wcoeffs(2 * nv + i, j + psize) -= wts[k] * p * pts(k, 0) * phi(0, j, k); } } } math::orthogonalise(wcoeffs, nv * 2); return wcoeffs; } //----------------------------------------------------------------------------- template impl::mdarray_t create_nedelec_3d_space(int degree) { // Reference tetrahedron const std::size_t tdim = 3; // Number of order (degree) vector polynomials const std::size_t nv = degree * (degree + 1) * (degree + 2) / 6; // Number of order (degree-1) vector polynomials const std::size_t ns0 = (degree - 1) * degree * (degree + 1) / 6; // Number of additional Nedelec polynomials that could be added const std::size_t ns = degree * (degree + 1) / 2; // Number of polynomials that would be included that are not // independent so are removed const std::size_t ns_remove = degree * (degree - 1) / 2; // Number of dofs in the space, ie size of polynomial set const std::size_t ndofs = 6 * degree + 4 * degree * (degree - 1) + (degree - 2) * (degree - 1) * degree / 2; // Tabulate polynomial basis at quadrature points const auto [_pts, wts] = quadrature::make_quadrature( quadrature::type::Default, cell::type::tetrahedron, polyset::type::standard, 2 * degree); impl::mdspan_t pts(_pts.data(), wts.size(), _pts.size() / wts.size()); const auto [_phi, shape] = polyset::tabulate( cell::type::tetrahedron, polyset::type::standard, degree, 0, pts); impl::mdspan_t phi(_phi.data(), shape); const std::size_t psize = phi.extent(1); // Create coefficients for order (degree-1) polynomials impl::mdarray_t wcoeffs(ndofs, psize * tdim); for (std::size_t i = 0; i < tdim; ++i) for (std::size_t j = 0; j < nv; ++j) wcoeffs(i * nv + j, i * psize + j) = 1.0; // Create coefficients for additional Nedelec polynomials for (std::size_t i = 0; i < ns; ++i) { for (std::size_t j = nv; j < psize; ++j) { T w = 0.0; for (std::size_t k = 0; k < wts.size(); ++k) w += wts[k] * phi(0, ns0 + i, k) * pts(k, 2) * phi(0, j, k); // Don't include polynomials (*, *, 0) that are dependent if (i >= ns_remove) wcoeffs(tdim * nv + i - ns_remove, psize + j) = -w; wcoeffs(tdim * nv + i + ns - ns_remove, j) = w; } } for (std::size_t i = 0; i < ns; ++i) { for (std::size_t j = nv; j < psize; ++j) { T w = 0.0; for (std::size_t k = 0; k < wts.size(); ++k) w += wts[k] * phi(0, ns0 + i, k) * pts(k, 1) * phi(0, j, k); wcoeffs(tdim * nv + i + ns * 2 - ns_remove, j) = -w; // Don't include polynomials (*, *, 0) that are dependent if (i >= ns_remove) wcoeffs(tdim * nv + i - ns_remove, psize * 2 + j) = w; } } for (std::size_t i = 0; i < ns; ++i) { for (std::size_t j = nv; j < psize; ++j) { T w = 0.0; for (std::size_t k = 0; k < wts.size(); ++k) w += wts[k] * phi(0, ns0 + i, k) * pts(k, 0) * phi(0, j, k); wcoeffs(tdim * nv + i + ns - ns_remove, psize * 2 + j) = -w; wcoeffs(tdim * nv + i + ns * 2 - ns_remove, psize + j) = w; } } math::orthogonalise(wcoeffs, nv * 3); return wcoeffs; } //----------------------------------------------------------------------------- } // namespace //----------------------------------------------------------------------------- template FiniteElement element::create_nedelec(cell::type celltype, int degree, lagrange_variant lvariant, bool discontinuous) { if (degree < 1) throw std::runtime_error("Degree must be at least 1"); const std::size_t tdim = cell::topological_dimension(celltype); std::array>, 4> x; std::array>, 4> M; { const std::size_t num_ent = cell::num_sub_entities(celltype, 0); x[0] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[0] = std::vector(num_ent, impl::mdarray_t(0, tdim, 0, 1)); } std::vector wcoeffs; std::array wshape; switch (celltype) { case cell::type::triangle: { impl::mdarray_t w = create_nedelec_2d_space(degree); wshape = {w.extent(0), w.extent(1)}; wcoeffs.resize(wshape[0] * wshape[1]); std::copy_n(w.data(), w.size(), wcoeffs.data()); break; } case cell::type::tetrahedron: { impl::mdarray_t w = create_nedelec_3d_space(degree); wshape = {w.extent(0), w.extent(1)}; wcoeffs.resize(wshape[0] * wshape[1]); std::copy_n(w.data(), w.size(), wcoeffs.data()); break; } default: throw std::runtime_error("Invalid celltype in Nedelec"); } // Integral representation for the boundary (edge) dofs { FiniteElement edge_space = element::create_lagrange( cell::type::interval, degree - 1, lvariant, true); auto [_x, xshape, _M, Mshape] = moments::make_tangent_integral_moments( edge_space, celltype, polyset::type::standard, tdim, 2 * degree - 1); assert(_x.size() == _M.size()); for (std::size_t i = 0; i < _x.size(); ++i) { x[1].emplace_back(std::array{xshape[0], xshape[1]}, _x[i]); M[1].emplace_back(std::array{Mshape[0], Mshape[1], Mshape[2], Mshape[3]}, _M[i]); } } // Face dofs if (degree > 1) { FiniteElement face_space = element::create_lagrange( cell::type::triangle, degree - 2, lvariant, true); auto [_x, xshape, _M, Mshape] = moments::make_integral_moments( face_space, celltype, polyset::type::standard, tdim, 2 * degree - 2); assert(_x.size() == _M.size()); for (std::size_t i = 0; i < _x.size(); ++i) { x[2].emplace_back(std::array{xshape[0], xshape[1]}, _x[i]); M[2].emplace_back(std::array{Mshape[0], Mshape[1], Mshape[2], Mshape[3]}, _M[i]); } } else { const std::size_t num_ent = cell::num_sub_entities(celltype, 2); x[2] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[2] = std::vector(num_ent, impl::mdarray_t(0, tdim, 0, 1)); } // Volume dofs if (tdim == 3) { if (degree > 2 and tdim == 3) { auto [_x, xshape, _M, Mshape] = moments::make_integral_moments( element::create_lagrange(cell::type::tetrahedron, degree - 3, lvariant, true), cell::type::tetrahedron, polyset::type::standard, 3, 2 * degree - 3); assert(_x.size() == _M.size()); for (std::size_t i = 0; i < _x.size(); ++i) { x[3].emplace_back(std::array{xshape[0], xshape[1]}, _x[i]); M[3].emplace_back( std::array{Mshape[0], Mshape[1], Mshape[2], Mshape[3]}, _M[i]); } } else { const std::size_t num_ent = cell::num_sub_entities(celltype, 3); x[3] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[3] = std::vector(num_ent, impl::mdarray_t(0, tdim, 0, 1)); } } std::array>, 4> xview = impl::to_mdspan(x); std::array>, 4> Mview = impl::to_mdspan(M); std::array>, 4> xbuffer; std::array>, 4> Mbuffer; if (discontinuous) { // std::tie(x, M) = element::make_discontinuous(x, M, tdim, tdim); std::array>, 4> xshape; std::array>, 4> Mshape; std::tie(xbuffer, xshape, Mbuffer, Mshape) = element::make_discontinuous(xview, Mview, tdim, tdim); xview = impl::to_mdspan(xbuffer, xshape); Mview = impl::to_mdspan(Mbuffer, Mshape); } sobolev::space space = discontinuous ? sobolev::space::L2 : sobolev::space::HCurl; return FiniteElement( element::family::N1E, celltype, polyset::type::standard, degree, {tdim}, impl::mdspan_t(wcoeffs.data(), wshape), xview, Mview, 0, maps::type::covariantPiola, space, discontinuous, degree - 1, degree, lvariant, element::dpc_variant::unset); } //----------------------------------------------------------------------------- template FiniteElement element::create_nedelec2(cell::type celltype, int degree, lagrange_variant lvariant, bool discontinuous) { if (celltype != cell::type::triangle and celltype != cell::type::tetrahedron) throw std::runtime_error("Invalid celltype in Nedelec"); if (degree < 1) throw std::runtime_error("Degree must be at least 1"); std::array>, 4> x; std::array>, 4> M; const std::size_t tdim = cell::topological_dimension(celltype); { const std::size_t num_ent = cell::num_sub_entities(celltype, 0); x[0] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[0] = std::vector(num_ent, impl::mdarray_t(0, tdim, 0, 1)); } // Integral representation for the edge dofs { FiniteElement edge_space = element::create_lagrange( cell::type::interval, degree, lvariant, true); auto [_x, xshape, _M, Mshape] = moments::make_tangent_integral_moments( edge_space, celltype, polyset::type::standard, tdim, 2 * degree); assert(_x.size() == _M.size()); for (std::size_t i = 0; i < _x.size(); ++i) { x[1].emplace_back(std::array{xshape[0], xshape[1]}, _x[i]); M[1].emplace_back(std::array{Mshape[0], Mshape[1], Mshape[2], Mshape[3]}, _M[i]); } } if (degree > 1) { // Integral moments on faces FiniteElement face_space = element::create_rt( cell::type::triangle, degree - 1, lvariant, true); auto [_x, xshape, _M, Mshape] = moments::make_dot_integral_moments( face_space, celltype, polyset::type::standard, tdim, 2 * degree - 1); assert(_x.size() == _M.size()); for (std::size_t i = 0; i < _x.size(); ++i) { x[2].emplace_back(std::array{xshape[0], xshape[1]}, _x[i]); M[2].emplace_back(std::array{Mshape[0], Mshape[1], Mshape[2], Mshape[3]}, _M[i]); } } else { const std::size_t num_ent = cell::num_sub_entities(celltype, 2); x[2] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[2] = std::vector(num_ent, impl::mdarray_t(0, tdim, 0, 1)); } if (tdim == 3) { if (degree > 2) { auto [_x, xshape, _M, Mshape] = moments::make_dot_integral_moments( element::create_rt(cell::type::tetrahedron, degree - 2, lvariant, true), celltype, polyset::type::standard, tdim, 2 * degree - 2); assert(_x.size() == _M.size()); for (std::size_t i = 0; i < _x.size(); ++i) { x[3].emplace_back(std::array{xshape[0], xshape[1]}, _x[i]); M[3].emplace_back( std::array{Mshape[0], Mshape[1], Mshape[2], Mshape[3]}, _M[i]); } } else { const std::size_t num_ent = cell::num_sub_entities(celltype, 3); x[3] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[3] = std::vector(num_ent, impl::mdarray_t(0, tdim, 0, 1)); } } std::array>, 4> xview = impl::to_mdspan(x); std::array>, 4> Mview = impl::to_mdspan(M); std::array>, 4> xbuffer; std::array>, 4> Mbuffer; if (discontinuous) { std::array>, 4> xshape; std::array>, 4> Mshape; std::tie(xbuffer, xshape, Mbuffer, Mshape) = element::make_discontinuous(xview, Mview, tdim, tdim); xview = impl::to_mdspan(xbuffer, xshape); Mview = impl::to_mdspan(Mbuffer, Mshape); } const std::size_t psize = polyset::dim(celltype, polyset::type::standard, degree); return FiniteElement( element::family::N2E, celltype, polyset::type::standard, degree, {tdim}, impl::mdspan_t(math::eye(tdim * psize).data(), tdim * psize, tdim * psize), xview, Mview, 0, maps::type::covariantPiola, sobolev::space::HCurl, discontinuous, degree, degree, lvariant, element::dpc_variant::unset); } //----------------------------------------------------------------------------- template FiniteElement element::create_nedelec(cell::type, int, lagrange_variant, bool); template FiniteElement element::create_nedelec(cell::type, int, lagrange_variant, bool); template FiniteElement element::create_nedelec2(cell::type, int, lagrange_variant, bool); template FiniteElement element::create_nedelec2(cell::type, int, lagrange_variant, bool); //----------------------------------------------------------------------------- fenics-basix-0.9.0/cpp/basix/e-nedelec.h000066400000000000000000000025041470517546000177670ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson // FEniCS Project // SPDX-License-Identifier: MIT #pragma once #include "cell.h" #include "element-families.h" #include "finite-element.h" #include namespace basix::element { /// Create Nedelec element (first kind) /// @param[in] celltype The cell type /// @param[in] degree The degree of the element /// @param[in] lvariant The Lagrange variant to use when defining the /// element to take integral moments against /// @param[in] discontinuous Controls whether the element is continuous /// or discontinuous /// @return A finite element template FiniteElement create_nedelec(cell::type celltype, int degree, lagrange_variant lvariant, bool discontinuous); /// Create Nedelec element (second kind) /// @param[in] celltype The cell type /// @param[in] degree The degree of the element /// @param[in] lvariant The Lagrange variant to use when defining the /// element to take integral moments against /// @param[in] discontinuous Controls whether the element is continuous /// or discontinuous /// @return A finite element template FiniteElement create_nedelec2(cell::type celltype, int degree, lagrange_variant lvariant, bool discontinuous); } // namespace basix::element fenics-basix-0.9.0/cpp/basix/e-raviart-thomas.cpp000066400000000000000000000134631470517546000216720ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson & Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #include "e-raviart-thomas.h" #include "e-lagrange.h" #include "element-families.h" #include "maps.h" #include "math.h" #include "moments.h" #include "polyset.h" #include "quadrature.h" #include "sobolev-spaces.h" #include #include using namespace basix; //---------------------------------------------------------------------------- template FiniteElement basix::element::create_rt(cell::type celltype, int degree, element::lagrange_variant lvariant, bool discontinuous) { if (celltype != cell::type::triangle and celltype != cell::type::tetrahedron) throw std::runtime_error("Unsupported cell type"); if (degree < 1) throw std::runtime_error("Degree must be at least 1"); const std::size_t tdim = cell::topological_dimension(celltype); const cell::type facettype = (tdim == 2) ? cell::type::interval : cell::type::triangle; // The number of order (degree-1) scalar polynomials const std::size_t nv = polyset::dim(celltype, polyset::type::standard, degree - 1); // The number of order (degree-2) scalar polynomials const std::size_t ns0 = polyset::dim(celltype, polyset::type::standard, degree - 2); // The number of additional polynomials in the polynomial basis for // Raviart-Thomas const std::size_t ns = polyset::dim(facettype, polyset::type::standard, degree - 1); // Evaluate the expansion polynomials at the quadrature points const auto [_pts, wts] = quadrature::make_quadrature( quadrature::type::Default, celltype, polyset::type::standard, 2 * degree); impl::mdspan_t pts(_pts.data(), wts.size(), _pts.size() / wts.size()); const auto [_phi, shape] = polyset::tabulate(celltype, polyset::type::standard, degree, 0, pts); impl::mdspan_t phi(_phi.data(), shape); // The number of order (degree) polynomials const std::size_t psize = phi.extent(1); // Create coefficients for order (degree-1) vector polynomials impl::mdarray_t B(nv * tdim + ns, psize * tdim); for (std::size_t i = 0; i < tdim; ++i) for (std::size_t j = 0; j < nv; ++j) B(nv * i + j, psize * i + j) = 1.0; // Create coefficients for additional polynomials in Raviart-Thomas // polynomial basis for (std::size_t i = 0; i < ns; ++i) { for (std::size_t k = nv; k < psize; ++k) { for (std::size_t j = 0; j < tdim; ++j) { B(nv * tdim + i, k + psize * j) = 0.0; for (std::size_t k1 = 0; k1 < wts.size(); ++k1) { B(nv * tdim + i, k + psize * j) += wts[k1] * phi(0, ns0 + i, k1) * pts(k1, j) * phi(0, k, k1); } } } } math::orthogonalise(B, nv * tdim); std::array>, 4> x; std::array>, 4> M; for (std::size_t i = 0; i < tdim - 1; ++i) { const std::size_t num_ent = cell::num_sub_entities(celltype, i); x[i] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[i] = std::vector(num_ent, impl::mdarray_t(0, tdim, 0, 1)); } // Add integral moments on facets { const FiniteElement facet_moment_space = element::create_lagrange(facettype, degree - 1, lvariant, true); auto [_x, xshape, _M, Mshape] = moments::make_normal_integral_moments( facet_moment_space, celltype, polyset::type::standard, tdim, 2 * degree - 1); assert(_x.size() == _M.size()); for (std::size_t i = 0; i < _x.size(); ++i) { x[tdim - 1].emplace_back(xshape, _x[i]); M[tdim - 1].emplace_back(Mshape, _M[i]); } } // Add integral moments on interior if (degree > 1) { // Interior integral moment auto [_x, xshape, _M, Mshape] = moments::make_integral_moments( element::create_lagrange(celltype, degree - 2, lvariant, true), celltype, polyset::type::standard, tdim, 2 * degree - 2); assert(_x.size() == _M.size()); for (std::size_t i = 0; i < _x.size(); ++i) { x[tdim].emplace_back(xshape, _x[i]); M[tdim].emplace_back(Mshape, _M[i]); } } else { const std::size_t num_ent = cell::num_sub_entities(celltype, tdim); x[tdim] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[tdim] = std::vector(num_ent, impl::mdarray_t(0, tdim, 0, 1)); } std::array>, 4> xview = impl::to_mdspan(x); std::array>, 4> Mview = impl::to_mdspan(M); std::array>, 4> xbuffer; std::array>, 4> Mbuffer; if (discontinuous) { std::array>, 4> xshape; std::array>, 4> Mshape; std::tie(xbuffer, xshape, Mbuffer, Mshape) = element::make_discontinuous(xview, Mview, tdim, tdim); xview = impl::to_mdspan(xbuffer, xshape); Mview = impl::to_mdspan(Mbuffer, Mshape); } sobolev::space space = discontinuous ? sobolev::space::L2 : sobolev::space::HDiv; return FiniteElement( element::family::RT, celltype, polyset::type::standard, degree, {tdim}, impl::mdspan_t(B.data(), B.extents()), xview, Mview, 0, maps::type::contravariantPiola, space, discontinuous, degree - 1, degree, lvariant, element::dpc_variant::unset); } //----------------------------------------------------------------------------- template FiniteElement element::create_rt(cell::type, int, element::lagrange_variant, bool); template FiniteElement element::create_rt(cell::type, int, element::lagrange_variant, bool); //----------------------------------------------------------------------------- fenics-basix-0.9.0/cpp/basix/e-raviart-thomas.h000066400000000000000000000014671470517546000213400ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson // FEniCS Project // SPDX-License-Identifier: MIT #pragma once #include "cell.h" #include "element-families.h" #include "finite-element.h" #include namespace basix::element { /// Create Raviart-Thomas element /// @param[in] celltype The cell type /// @param[in] degree The degree of the element /// @param[in] lvariant The Lagrange variant to use when defining the element to /// take integral moments against /// @param[in] discontinuous Controls whether the element is continuous or /// discontinuous /// @return A finite element template FiniteElement create_rt(cell::type celltype, int degree, element::lagrange_variant lvariant, bool discontinuous); } // namespace basix::element fenics-basix-0.9.0/cpp/basix/e-regge.cpp000066400000000000000000000162111470517546000200140ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson // FEniCS Project // SPDX-License-Identifier: MIT #include "e-regge.h" #include "e-lagrange.h" #include "element-families.h" #include "maps.h" #include "math.h" #include "polyset.h" #include "quadrature.h" #include "sobolev-spaces.h" #include using namespace basix; namespace stdex = MDSPAN_IMPL_STANDARD_NAMESPACE::MDSPAN_IMPL_PROPOSED_NAMESPACE; //----------------------------------------------------------------------------- template FiniteElement element::create_regge(cell::type celltype, int degree, bool discontinuous) { if (celltype != cell::type::triangle and celltype != cell::type::tetrahedron) throw std::runtime_error("Unsupported celltype"); const std::size_t tdim = cell::topological_dimension(celltype); const int nc = tdim * (tdim + 1) / 2; const int basis_size = polyset::dim(celltype, polyset::type::standard, degree); const std::size_t ndofs = basis_size * nc; const std::size_t psize = basis_size * tdim * tdim; impl::mdarray_t wcoeffs(ndofs, psize); for (std::size_t i = 0; i < tdim; ++i) { for (std::size_t j = 0; j < tdim; ++j) { int xoff = i + tdim * j; int yoff = i + j; if (tdim == 3 and i > 0 and j > 0) ++yoff; std::size_t s = basis_size; for (std::size_t k = 0; k < s; ++k) wcoeffs(yoff * s + k, xoff * s + k) = i == j ? 1.0 : std::sqrt(0.5); } } const std::vector>> topology = cell::topology(celltype); const auto [gbuffer, gshape] = cell::geometry(celltype); impl::mdspan_t geometry(gbuffer.data(), gshape); std::array>, 4> x; std::array>, 4> M; { const std::size_t num_ent = cell::num_sub_entities(celltype, 0); x[0] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[0] = std::vector(num_ent, impl::mdarray_t(0, tdim * tdim, 0, 1)); } // Loop over edge and higher dimension entities for (std::size_t d = 1; d < topology.size(); ++d) { if (static_cast(degree) + 1 < d) { for (std::size_t e = 0; e < topology[d].size(); ++e) { x[d].emplace_back(0, tdim); M[d].emplace_back(0, tdim * tdim, 0, 1); } } else { // Loop over entities of dimension dim for (std::size_t e = 0; e < topology[d].size(); ++e) { // Entity coordinates const auto [ebuffer, eshape] = cell::sub_entity_geometry(celltype, d, e); impl::mdspan_t entity_x(ebuffer.data(), eshape); // Tabulate points in lattice cell::type ct = cell::sub_entity_type(celltype, d, e); const std::size_t ndofs = polyset::dim(ct, polyset::type::standard, degree + 1 - d); const auto [_pts, wts] = quadrature::make_quadrature( quadrature::type::Default, ct, polyset::type::standard, degree + (degree + 1 - d)); impl::mdspan_t pts(_pts.data(), wts.size(), _pts.size() / wts.size()); FiniteElement moment_space = create_lagrange( ct, degree + 1 - d, element::lagrange_variant::legendre, true); const auto [phib, phishape] = moment_space.tabulate(0, pts); impl::mdspan_t moment_values(phib.data(), phishape); auto& _x = x[d].emplace_back(pts.extent(0), tdim); // Copy points for (std::size_t p = 0; p < pts.extent(0); ++p) { for (std::size_t j = 0; j < entity_x.extent(1); ++j) _x(p, j) = entity_x(0, j); for (std::size_t i = 0; i < entity_x.extent(0) - 1; ++i) for (std::size_t j = 0; j < entity_x.extent(1); ++j) _x(p, j) += (entity_x(i + 1, j) - entity_x(0, j)) * pts(p, i); } // Store up outer(t, t) for all tangents const std::vector& vert_ids = topology[d][e]; const std::size_t ntangents = d * (d + 1) / 2; stdex::mdarray> vvt(ntangents, geometry.extent(1), geometry.extent(1)); std::vector edge(geometry.extent(1)); int c = 0; for (std::size_t s = 0; s < d; ++s) { for (std::size_t r = s + 1; r < d + 1; ++r) { for (std::size_t p = 0; p < geometry.extent(1); ++p) edge[p] = geometry(vert_ids[r], p) - geometry(vert_ids[s], p); // outer product v.v^T auto [buffer, shape] = math::outer(edge, edge); impl::mdspan_t result(buffer.data(), shape); for (std::size_t i = 0; i < vvt.extent(1); ++i) for (std::size_t j = 0; j < vvt.extent(2); ++j) vvt(c, i, j) = result(i, j); ++c; } } auto& _M = M[d].emplace_back(ndofs * ntangents, tdim * tdim, pts.extent(0), 1); for (int n = 0; n < moment_space.dim(); ++n) { for (std::size_t j = 0; j < ntangents; ++j) { std::vector vvt_flat; for (std::size_t i = 0; i < vvt.extent(1); ++i) for (std::size_t k = 0; k < vvt.extent(2); ++k) vvt_flat.push_back(vvt(j, i, k)); for (std::size_t q = 0; q < pts.extent(0); ++q) { for (std::size_t i = 0; i < tdim * tdim; ++i) { _M(n * ntangents + j, i, q, 0) = vvt_flat[i] * wts[q] * moment_values(0, q, n, 0); } } } } } } } // Regge has (d+1) dofs on each edge, 3d(d+1)/2 on each face and // d(d-1)(d+1) on the interior in 3D std::array>, 4> xview = impl::to_mdspan(x); std::array>, 4> Mview = impl::to_mdspan(M); std::array>, 4> xbuffer; std::array>, 4> Mbuffer; if (discontinuous) { std::array>, 4> xshape; std::array>, 4> Mshape; std::tie(xbuffer, xshape, Mbuffer, Mshape) = element::make_discontinuous(xview, Mview, tdim, tdim * tdim); xview = impl::to_mdspan(xbuffer, xshape); Mview = impl::to_mdspan(Mbuffer, Mshape); } sobolev::space space = discontinuous ? sobolev::space::L2 : sobolev::space::HEin; return FiniteElement( element::family::Regge, celltype, polyset::type::standard, degree, {tdim, tdim}, impl::mdspan_t(wcoeffs.data(), wcoeffs.extents()), xview, Mview, 0, maps::type::doubleCovariantPiola, space, discontinuous, -1, degree, element::lagrange_variant::unset, element::dpc_variant::unset); } //----------------------------------------------------------------------------- template FiniteElement element::create_regge(cell::type, int, bool); template FiniteElement element::create_regge(cell::type, int, bool); //----------------------------------------------------------------------------- fenics-basix-0.9.0/cpp/basix/e-regge.h000066400000000000000000000011441470517546000174600ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson // FEniCS Project // SPDX-License-Identifier: MIT #pragma once #include "cell.h" #include "finite-element.h" #include namespace basix::element { /// Create Regge element /// @param[in] celltype The cell type /// @param[in] degree The degree of the element /// @param[in] discontinuous Controls whether the element is continuous or /// discontinuous /// @return A finite element template FiniteElement create_regge(cell::type celltype, int degree, bool discontinuous); } // namespace basix::element fenics-basix-0.9.0/cpp/basix/e-serendipity.cpp000066400000000000000000001432701470517546000212700ustar00rootroot00000000000000// Copyright (c) 2021 Chris Richardson & Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #include "e-serendipity.h" #include "e-lagrange.h" #include "element-families.h" #include "lattice.h" #include "maps.h" #include "math.h" #include "mdspan.hpp" #include "moments.h" #include "polynomials.h" #include "polyset.h" #include "quadrature.h" #include "sobolev-spaces.h" #include using namespace basix; namespace { //---------------------------------------------------------------------------- template impl::mdarray_t make_serendipity_space_2d(int degree) { const std::size_t ndofs = degree == 1 ? 4 : degree * (degree + 3) / 2 + 3; // Evaluate the expansion polynomials at the quadrature points const auto [_pts, wts] = quadrature::make_quadrature( quadrature::type::Default, cell::type::quadrilateral, polyset::type::standard, 2 * degree); impl::mdspan_t pts(_pts.data(), wts.size(), _pts.size() / wts.size()); const auto [_Pq, shape] = polyset::tabulate( cell::type::quadrilateral, polyset::type::standard, degree, 0, pts); impl::mdspan_t Pq(_Pq.data(), shape); const std::size_t psize = Pq.extent(1); // Create coefficients for order (degree) polynomials impl::mdarray_t wcoeffs(ndofs, psize); int row_n = 0; for (int i = 0; i <= degree; ++i) for (int j = 0; j <= degree - i + (i == 1 || i == degree ? 1 : 0); ++j) wcoeffs(row_n++, i * (degree + 1) + j) = 1; assert(std::size_t(row_n) == ndofs); return wcoeffs; } //---------------------------------------------------------------------------- template impl::mdarray_t make_serendipity_space_3d(int degree) { const std::size_t ndofs = degree == 0 ? 1 : (degree < 4 ? 12 * degree - 4 : (degree < 6 ? 3 * degree * degree - 3 * degree + 14 : degree * (degree - 1) * (degree + 1) / 6 + degree * degree + 5 * degree + 4)); // Number of order (degree) polynomials // Evaluate the expansion polynomials at the quadrature points const auto [_pts, wts] = quadrature::make_quadrature( quadrature::type::Default, cell::type::hexahedron, polyset::type::standard, 2 * degree); impl::mdspan_t pts(_pts.data(), wts.size(), _pts.size() / wts.size()); const auto [_Ph, shape] = polyset::tabulate( cell::type::hexahedron, polyset::type::standard, degree, 0, pts); impl::mdspan_t Ph(_Ph.data(), shape); const std::size_t psize = Ph.extent(1); // Create coefficients for order (degree) polynomials impl::mdarray_t wcoeffs(ndofs, psize); int row_n = 0; for (int i = 0; i <= degree; ++i) for (int j = 0; j <= degree - i + (i == 1 || i == degree ? 1 : 0); ++j) for (int k = 0; k <= degree - i - j + (i == 1 || i == degree ? 1 : 0) + (j == 1 || j == degree ? 1 : 0) + (i + j == degree && i != 1 && i != degree && j != 1 && j != degree ? 1 : 0); ++k) wcoeffs(row_n++, i * (degree + 1) * (degree + 1) + j * (degree + 1) + k) = 1; assert((std::size_t)row_n == ndofs); return wcoeffs; } //---------------------------------------------------------------------------- template impl::mdarray_t make_serendipity_div_space_2d(int degree) { const std::size_t ndofs = degree * (degree + 3) + 4; // Evaluate the expansion polynomials at the quadrature points auto [_pts, wts] = quadrature::make_quadrature( quadrature::type::Default, cell::type::quadrilateral, polyset::type::standard, 2 * degree + 2); impl::mdspan_t pts(_pts.data(), wts.size(), _pts.size() / wts.size()); const auto [_Pq, shape] = polyset::tabulate( cell::type::quadrilateral, polyset::type::standard, degree + 1, 0, pts); impl::mdspan_t Pq(_Pq.data(), shape); const std::size_t psize = Pq.extent(1); const std::size_t nv = polyset::dim(cell::type::triangle, polyset::type::standard, degree); // Create coefficients for order (degree) vector polynomials impl::mdarray_t wcoeffs(ndofs, psize * 2); int row_n = 0; for (int i = 0; i <= degree; ++i) for (int j = 0; j <= degree - i; ++j) for (int d = 0; d < 2; ++d) wcoeffs(row_n++, d * psize + i * (degree + 2) + j) = 1; std::vector nonzero; for (int i = 0; i <= degree + 1; ++i) for (int j = i <= degree ? degree + 1 - i : 0; j <= degree + 1; ++j) nonzero.push_back(i * (degree + 2) + j); std::vector integrand(wts.size()); for (std::size_t k = 0; k < nonzero.size(); ++k) { for (std::size_t d = 0; d < 2; ++d) { for (std::size_t a = 0; a < 2; ++a) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] = wts[i] * Pq(0, nonzero[k], i); if (a == 0 and d == 0) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= pts(i, 0); } else if (a == 0 and d == 1) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= (degree + 1) * pts(i, 1); } else if (a == 1 and d == 0) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= (degree + 1) * pts(i, 0); } else if (a == 1 and d == 1) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= pts(i, 1); } for (int i = 0; i < degree; ++i) for (std::size_t j = 0; j < integrand.size(); ++j) integrand[j] *= pts(j, a); wcoeffs(2 * nv + a, psize * d + nonzero[k]) = std::reduce(integrand.begin(), integrand.end(), 0.0); } } } math::orthogonalise(impl::mdspan_t(wcoeffs.data(), wcoeffs.extent(0), wcoeffs.extent(1)), 2 * nv); return wcoeffs; } //---------------------------------------------------------------------------- template impl::mdarray_t make_serendipity_div_space_3d(int degree) { const std::size_t ndofs = (degree + 1) * (degree * (degree + 5) + 12) / 2; // Evaluate the expansion polynomials at the quadrature points const auto [_pts, wts] = quadrature::make_quadrature( quadrature::type::Default, cell::type::hexahedron, polyset::type::standard, 2 * degree + 2); impl::mdspan_t pts(_pts.data(), wts.size(), _pts.size() / wts.size()); const auto [_Pq, shape] = polyset::tabulate( cell::type::hexahedron, polyset::type::standard, degree + 1, 0, pts); impl::mdspan_t Pq(_Pq.data(), shape); const std::size_t psize = Pq.extent(1); const std::size_t nv = polyset::dim(cell::type::tetrahedron, polyset::type::standard, degree); // Create coefficients for order (degree) vector polynomials impl::mdarray_t wcoeffs(ndofs, psize * 3); int row_n = 0; for (int i = 0; i <= degree; ++i) { for (int j = 0; j <= degree - i; ++j) { for (int k = 0; k <= degree - i - j; ++k) { for (int d = 0; d < 3; ++d) { wcoeffs(row_n++, d * psize + i * (degree + 2) * (degree + 2) + j * (degree + 2) + k) = 1; } } } } std::vector nonzero; for (int i = 0; i <= degree + 1; ++i) { for (int j = 0; j <= degree + 1; ++j) { for (int k = i + j <= degree ? degree + 1 - i - j : 0; k <= degree + 1; ++k) { nonzero.push_back(i * (degree + 2) * (degree + 2) + j * (degree + 2) + k); } } } std::vector integrand(wts.size()); for (std::size_t k = 0; k < nonzero.size(); ++k) { for (std::size_t d = 0; d < 3; ++d) { for (std::size_t a = 0; a < 3; ++a) { for (int index = 0; index <= degree; ++index) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] = wts[i] * Pq(0, nonzero[k], i); if (a == 0) { if (d == 0) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= -(degree + 2) * pts(i, 0); } else if (d == 1) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= pts(i, 1); } else if (d == 2) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= pts(i, 2); } for (int i = 0; i < index; ++i) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= pts(i, 1); } for (int i = 0; i < degree - index; ++i) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= pts(i, 2); } } else if (a == 1) { if (d == 0) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= -pts(i, 0); } else if (d == 1) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= (degree + 2) * pts(i, 1); } else if (d == 2) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= -pts(i, 2); } for (int i = 0; i < index; ++i) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= pts(i, 0); } for (int i = 0; i < degree - index; ++i) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= pts(i, 2); } } else if (a == 2) { if (d == 0) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= pts(i, 0); } else if (d == 1) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= pts(i, 1); } else if (d == 2) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= -(degree + 2) * pts(i, 2); } for (int i = 0; i < index; ++i) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= pts(i, 0); } for (int i = 0; i < degree - index; ++i) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= pts(i, 1); } } wcoeffs(3 * nv + 3 * index + a, psize * d + nonzero[k]) = std::reduce(integrand.begin(), integrand.end(), 0.0); } } } } math::orthogonalise(impl::mdspan_t(wcoeffs.data(), wcoeffs.extent(0), wcoeffs.extent(1)), 3 * nv); return wcoeffs; } //---------------------------------------------------------------------------- template impl::mdarray_t make_serendipity_curl_space_2d(int degree) { const std::size_t ndofs = degree * (degree + 3) + 4; // Evaluate the expansion polynomials at the quadrature points const auto [_pts, wts] = quadrature::make_quadrature( quadrature::type::Default, cell::type::quadrilateral, polyset::type::standard, 2 * degree + 2); impl::mdspan_t pts(_pts.data(), wts.size(), _pts.size() / wts.size()); const auto [_Pq, shape] = polyset::tabulate( cell::type::quadrilateral, polyset::type::standard, degree + 1, 0, pts); impl::mdspan_t Pq(_Pq.data(), shape); const std::size_t psize = Pq.extent(1); const std::size_t nv = polyset::dim(cell::type::triangle, polyset::type::standard, degree); // Create coefficients for order (degree) vector polynomials impl::mdarray_t wcoeffs(ndofs, psize * 2); int row_n = 0; for (int i = 0; i <= degree; ++i) for (int j = 0; j <= degree - i; ++j) for (int d = 0; d < 2; ++d) wcoeffs(row_n++, d * psize + i * (degree + 2) + j) = 1; std::vector nonzero; for (int i = 0; i <= degree + 1; ++i) for (int j = i <= degree ? degree + 1 - i : 0; j <= degree + 1; ++j) nonzero.push_back(i * (degree + 2) + j); std::vector integrand(wts.size()); for (std::size_t k = 0; k < nonzero.size(); ++k) { for (std::size_t d = 0; d < 2; ++d) { for (std::size_t a = 0; a < 2; ++a) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] = wts[i] * Pq(0, nonzero[k], i); if (a == 0 and d == 0) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= (degree + 1) * pts(i, 1); } else if (a == 0 and d == 1) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= -pts(i, 0); } else if (a == 1 and d == 0) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= pts(i, 1); } else if (a == 1 and d == 1) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= -(degree + 1) * pts(i, 0); } for (int i = 0; i < degree; ++i) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= pts(i, a); } wcoeffs(2 * nv + a, psize * d + nonzero[k]) = std::reduce(integrand.begin(), integrand.end(), 0.0); } } } math::orthogonalise(impl::mdspan_t(wcoeffs.data(), wcoeffs.extent(0), wcoeffs.extent(1)), 2 * nv); return wcoeffs; } //---------------------------------------------------------------------------- template impl::mdarray_t make_serendipity_curl_space_3d(int degree) { const std::size_t ndofs = degree <= 3 ? 6 * (degree * (degree + 1) + 2) : degree * (degree + 1) * (degree - 1) / 2 + 3 * (degree * (degree + 4) + 3); // Evaluate the expansion polynomials at the quadrature points const auto [_pts, wts] = quadrature::make_quadrature( quadrature::type::Default, cell::type::hexahedron, polyset::type::standard, 2 * degree + 2); impl::mdspan_t pts(_pts.data(), wts.size(), _pts.size() / wts.size()); const auto [_Pq, shape] = polyset::tabulate( cell::type::hexahedron, polyset::type::standard, degree + 1, 0, pts); impl::mdspan_t Pq(_Pq.data(), shape); const std::size_t psize = Pq.extent(1); const std::size_t nv = polyset::dim(cell::type::tetrahedron, polyset::type::standard, degree); // Create coefficients for order (degree) vector polynomials impl::mdarray_t wcoeffs(ndofs, psize * 3); int row_n = 0; for (int i = 0; i <= degree; ++i) { for (int j = 0; j <= degree - i; ++j) { for (int k = 0; k <= degree - i - j; ++k) { for (int d = 0; d < 3; ++d) { wcoeffs(row_n++, d * psize + i * (degree + 2) * (degree + 2) + j * (degree + 2) + k) = 1; } } } } std::vector nonzero; for (int i = 0; i <= degree + 1; ++i) { for (int j = 0; j <= degree + 1; ++j) { for (int k = i + j <= degree ? degree + 1 - i - j : 0; k <= degree + 1; ++k) { nonzero.push_back(i * (degree + 2) * (degree + 2) + j * (degree + 2) + k); } } } std::vector integrand(wts.size()); for (std::size_t k = 0; k < nonzero.size(); ++k) { for (std::size_t d = 0; d < 3; ++d) { for (std::size_t a = 0; a < (degree > 1 ? 3 : 2); ++a) { for (int index = 0; index <= degree; ++index) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] = wts[i] * Pq(0, nonzero[k], i); if (a == 0) { if (d == 0) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= pts(i, 1) * pts(i, 2); } else if (d == 1) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= 0; } else if (d == 2) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= -pts(i, 0) * pts(i, 1); } for (int i = 0; i < index; ++i) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= pts(i, 0); } for (int i = 0; i < degree - 1 - index; ++i) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= pts(i, 2); } } else if (a == 1) { if (d == 0) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= 0; } else if (d == 1) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= pts(i, 0) * pts(i, 2); } else if (d == 2) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= -pts(i, 0) * pts(i, 1); } for (int i = 0; i < index; ++i) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= pts(i, 1); } for (int i = 0; i < degree - 1 - index; ++i) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= pts(i, 2); } } else if (a == 2) { if (d == 0) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= pts(i, 1) * pts(i, 2); } else if (d == 1) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= -pts(i, 0) * pts(i, 2); } else if (d == 2) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= 0; } for (int i = 0; i < index; ++i) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= pts(i, 0); } for (int i = 0; i < degree - 1 - index; ++i) { for (std::size_t i = 0; i < integrand.size(); ++i) integrand[i] *= pts(i, 1); } } wcoeffs(3 * nv + 3 * index + a, psize * d + nonzero[k]) = std::reduce(integrand.begin(), integrand.end(), 0.0); } } } } int c = 3 * nv + (degree > 1 ? 3 : 2) * degree; std::vector> indices; for (int s = 1; s <= 3; ++s) { for (int i0 = 0; i0 <= s + degree + 1; ++i0) { for (int i1 = 0; i1 <= s + degree + 1 - i0; ++i1) { if ((i0 == 1 ? 1 : 0) + (i1 == 1 ? 1 : 0) + (s + degree == i0 + i1 ? 1 : 0) >= s) { std::array i = {i0, i1, s + degree + 1 - i0 - i1}; for (std::size_t k = 0; k < nonzero.size(); ++k) { for (int d = 0; d < 3; ++d) { for (std::size_t j = 0; j < integrand.size(); ++j) integrand[j] = wts[j] * Pq(0, nonzero[k], j); for (int d2 = 0; d2 < 3; ++d2) { if (d == d2) { for (std::size_t j = 0; j < integrand.size(); ++j) integrand[j] *= i[d2]; for (int j = 0; j < i[d2] - 1; ++j) { for (std::size_t j = 0; j < integrand.size(); ++j) integrand[j] *= pts(j, d2); } } else { for (int j = 0; j < i[d2]; ++j) { for (std::size_t j = 0; j < integrand.size(); ++j) integrand[j] *= pts(j, d2); } } } wcoeffs(c, psize * d + nonzero[k]) = std::reduce(integrand.begin(), integrand.end(), 0.0); } } ++c; } } } } assert((std::size_t)c == ndofs); math::orthogonalise(impl::mdspan_t(wcoeffs.data(), wcoeffs.extent(0), wcoeffs.extent(1)), 3 * nv); return wcoeffs; } //---------------------------------------------------------------------------- template FiniteElement create_legendre_dpc(cell::type celltype, int degree, bool discontinuous) { if (!discontinuous) throw std::runtime_error("Legendre variant must be discontinuous"); cell::type simplex_type; switch (celltype) { case cell::type::quadrilateral: simplex_type = cell::type::triangle; break; case cell::type::hexahedron: simplex_type = cell::type::tetrahedron; break; default: throw std::runtime_error("Invalid cell type"); } const std::size_t tdim = cell::topological_dimension(celltype); const std::size_t psize = polyset::dim(celltype, polyset::type::standard, degree); const std::size_t ndofs = polyset::dim(simplex_type, polyset::type::standard, degree); const auto [_pts, wts] = quadrature::make_quadrature( quadrature::type::Default, celltype, polyset::type::standard, degree * 2); impl::mdspan_t pts(_pts.data(), wts.size(), _pts.size() / wts.size()); // Evaluate moment space at quadrature points const auto [_phi, shape] = polynomials::tabulate(polynomials::type::legendre, celltype, degree, pts); impl::mdspan_t phi(_phi.data(), shape); std::array>, 4> x; std::array>, 4> M; for (std::size_t i = 0; i < tdim; ++i) { const std::size_t num_ent = cell::num_sub_entities(celltype, i); x[i] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[i] = std::vector(num_ent, impl::mdarray_t(0, 1, 0, 1)); } x[tdim].emplace_back(pts.extents(), _pts); auto& _M = M[tdim].emplace_back( std::array{ndofs, 1, pts.extent(0), 1}, 0); impl::mdarray_t wcoeffs(ndofs, psize); if (celltype == cell::type::quadrilateral) { int row_n = 0; for (int i = 0; i <= degree; ++i) { for (int j = 0; j <= degree - i; ++j) { for (std::size_t k = 0; k < wts.size(); ++k) _M(row_n, 0, k, 0) = phi(i * (degree + 1) + j, k) * wts[k]; wcoeffs(row_n, i * (degree + 1) + j) = 1; ++row_n; } } } else { int row_n = 0; for (int i = 0; i <= degree; ++i) { for (int j = 0; j <= degree - i; ++j) { for (int k = 0; k <= degree - i - j; ++k) { for (std::size_t l = 0; l < wts.size(); ++l) { _M(row_n, 0, l, 0) = phi(i * (degree + 1) * (degree + 1) + j * (degree + 1) + k, l) * wts[l]; } wcoeffs(row_n, i * (degree + 1) * (degree + 1) + j * (degree + 1) + k) = 1; ++row_n; } } } } return FiniteElement( element::family::DPC, celltype, polyset::type::standard, degree, {}, impl::mdspan_t(wcoeffs.data(), wcoeffs.extents()), impl::to_mdspan(x), impl::to_mdspan(M), 0, maps::type::identity, sobolev::space::L2, discontinuous, degree, degree, element::lagrange_variant::unset, element::dpc_variant::legendre); } //----------------------------------------------------------------------------- template impl::mdarray_t make_dpc_points(cell::type celltype, int degree, element::dpc_variant variant) { auto to_mdspan = [](auto& x, auto shape) { return impl::mdspan_t(x.data(), shape); }; auto to_mdarray = [](auto& x, auto shape) { return impl::mdarray_t(shape, x); }; if (degree == 0) { const auto [data, shape] = lattice::create(celltype, 0, lattice::type::equispaced, true); return to_mdarray(data, shape); } if (variant == element::dpc_variant::simplex_equispaced or variant == element::dpc_variant::simplex_gll) { lattice::type latticetype; lattice::simplex_method latticesm = lattice::simplex_method::isaac; if (variant == element::dpc_variant::simplex_equispaced) latticetype = lattice::type::equispaced; else if (variant == element::dpc_variant::simplex_gll) latticetype = lattice::type::gll; switch (celltype) { case cell::type::quadrilateral: { const auto [data, shape] = lattice::create( cell::type::triangle, degree, latticetype, true, latticesm); return to_mdarray(data, shape); } case cell::type::hexahedron: { const auto [data, shape] = lattice::create( cell::type::tetrahedron, degree, latticetype, true, latticesm); return to_mdarray(data, shape); } default: throw std::runtime_error("Invalid cell type"); } } else if (variant == element::dpc_variant::horizontal_equispaced or variant == element::dpc_variant::horizontal_gll) { lattice::type latticetype; if (variant == element::dpc_variant::horizontal_equispaced) latticetype = lattice::type::equispaced; else if (variant == element::dpc_variant::horizontal_gll) latticetype = lattice::type::gll; switch (celltype) { case cell::type::quadrilateral: { impl::mdarray_t pts((degree + 2) * (degree + 1) / 2, 2); std::size_t n = 0; for (int j = 0; j <= degree; ++j) { const auto [data, shape] = lattice::create( cell::type::interval, degree - j, latticetype, true); auto interval_pts = to_mdspan(data, shape); for (int i = 0; i <= degree - j; ++i) { pts(n, 0) = interval_pts(i, 0); pts(n, 1) = j % 2 == 0 ? static_cast(j / 2) / degree : 1 - static_cast((j - 1) / 2) / degree; ++n; } } return pts; } case cell::type::hexahedron: { impl::mdarray_t pts((degree + 3) * (degree + 2) * (degree + 1) / 6, 3); std::size_t n = 0; for (int k = 0; k <= degree; ++k) { for (int j = 0; j <= degree - k; ++j) { const auto [data, shape] = lattice::create( cell::type::interval, degree - j - k, latticetype, true); auto interval_pts = to_mdspan(data, shape); for (int i = 0; i <= degree - j - k; ++i) { pts(n, 0) = interval_pts(i, 0); pts(n, 1) = degree - k == 0 ? 0.5 : (j % 2 == 0 ? static_cast(j / 2) / (degree - k) : 1 - static_cast((j - 1) / 2) / (degree - k)); pts(n, 2) = k % 2 == 0 ? static_cast(k / 2) / degree : 1 - static_cast((k - 1) / 2) / degree; ++n; } } } return pts; } default: throw std::runtime_error("Invalid cell type"); } } else if (variant == element::dpc_variant::diagonal_equispaced or variant == element::dpc_variant::diagonal_gll) { lattice::type latticetype; lattice::simplex_method latticesm = lattice::simplex_method::isaac; if (variant == element::dpc_variant::diagonal_equispaced) latticetype = lattice::type::equispaced; else if (variant == element::dpc_variant::diagonal_gll) latticetype = lattice::type::gll; switch (celltype) { case cell::type::quadrilateral: { impl::mdarray_t pts((degree + 2) * (degree + 1) / 2, 2); const T gap = static_cast(2 * (degree + 1)) / (degree * degree + degree + 1); std::size_t n = 0; for (int j = 0; j <= degree; ++j) { const auto [data, shape] = lattice::create(cell::type::interval, j, latticetype, true); auto interval_pts = to_mdspan(data, shape); const T y = gap * (j % 2 == 0 ? j / 2 : degree - (j - 1) / 2); const T coord0 = y < 1 ? y : y - 1; const T coord1 = y < 1 ? 0 : 1; for (int i = 0; i <= j; ++i) { const T x = interval_pts(i, 0); pts(n, 0) = coord0 * (1 - x) + coord1 * x; pts(n, 1) = coord1 * (1 - x) + coord0 * x; ++n; } } return pts; } case cell::type::hexahedron: { impl::mdarray_t pts((degree + 3) * (degree + 2) * (degree + 1) / 6, 3); const T gap = static_cast(3 * degree) / (degree * degree + 1); std::size_t n = 0; for (int k = 0; k <= degree; ++k) { const T z = gap * (k % 2 == 0 ? k / 2 : degree - (k - 1) / 2); const auto [data, shape] = lattice::create( cell::type::triangle, k, latticetype, true, latticesm); auto triangle_pts = to_mdspan(data, shape); if (z < 1) { for (std::size_t p = 0; p < triangle_pts.extent(0); ++p) { const T coord0 = triangle_pts(p, 0); const T coord1 = triangle_pts(p, 1); pts(n, 0) = coord0 * z; pts(n, 1) = coord1 * z; pts(n, 2) = (1 - coord0 - coord1) * z; ++n; } } else if (z > 2) { for (std::size_t p = 0; p < triangle_pts.extent(0); ++p) { const T coord0 = triangle_pts(p, 0); const T coord1 = triangle_pts(p, 1); pts(n, 0) = 1 - (3 - z) * coord0; pts(n, 1) = 1 - (3 - z) * coord1; pts(n, 2) = 1 - (3 - z) * (1 - coord0 - coord1); ++n; } } else { for (std::size_t p = 0; p < triangle_pts.extent(0); ++p) { const T coord0 = triangle_pts(p, 0); const T coord1 = triangle_pts(p, 1); pts(n, 0) = 1 - (2 - z) * coord0 - coord1; pts(n, 1) = coord0 + (z - 1) * coord1; pts(n, 2) = z - 1 - (z - 1) * coord0 + (2 - z) * coord1; ++n; } } } return pts; } default: throw std::runtime_error("Invalid cell type"); } } else throw std::runtime_error("Unsupported_variant"); } //---------------------------------------------------------------------------- } // namespace //---------------------------------------------------------------------------- template FiniteElement element::create_serendipity(cell::type celltype, int degree, element::lagrange_variant lvariant, element::dpc_variant dvariant, bool discontinuous) { if (degree == 0) throw std::runtime_error("Cannot create degree 0 serendipity"); if (celltype != cell::type::interval and celltype != cell::type::quadrilateral and celltype != cell::type::hexahedron) { throw std::runtime_error("Invalid celltype"); } if (lvariant == element::lagrange_variant::unset) { if (degree < 3) lvariant = element::lagrange_variant::equispaced; else throw std::runtime_error("serendipity elements of degree > 2 need to be " "given a Lagrange variant."); } if (dvariant == element::dpc_variant::unset and celltype != cell::type::interval) { if (degree == 4) dvariant = element::dpc_variant::simplex_equispaced; if (degree > 4) throw std::runtime_error( "serendipity elements of degree > 4 need to be given a DPC variant."); } const std::size_t tdim = cell::topological_dimension(celltype); std::array>, 4> x; std::array>, 4> M; // dim 0 (vertices) const auto [gdata, gshape] = cell::geometry(celltype); impl::mdspan_t geometry(gdata.data(), gshape); for (std::size_t i = 0; i < geometry.extent(0); ++i) { auto& _x = x[0].emplace_back(1, geometry.extent(1)); for (std::size_t j = 0; j < geometry.extent(1); ++j) _x(0, j) = geometry(i, j); auto& _M = M[0].emplace_back(1, 1, 1, 1); _M(0, 0, 0, 0) = 1.0; } if (degree >= 2) { FiniteElement moment_space = element::create_lagrange( cell::type::interval, degree - 2, lvariant, true); auto [_x, xshape, _M, Mshape] = moments::make_integral_moments( moment_space, celltype, polyset::type::standard, 1, 2 * degree - 2); assert(_x.size() == _M.size()); for (std::size_t i = 0; i < _x.size(); ++i) { x[1].emplace_back(xshape, _x[i]); M[1].emplace_back(Mshape, _M[i]); } } else { const std::size_t num_ent = cell::num_sub_entities(celltype, 1); x[1] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[1] = std::vector(num_ent, impl::mdarray_t(0, 1, 0, 1)); } if (tdim >= 2) { if (degree >= 4) { FiniteElement moment_space = element::create_dpc( cell::type::quadrilateral, degree - 4, dvariant, true); auto [_x, xshape, _M, Mshape] = moments::make_integral_moments( moment_space, celltype, polyset::type::standard, 1, 2 * degree - 4); assert(_x.size() == _M.size()); for (std::size_t i = 0; i < _x.size(); ++i) { x[2].emplace_back(xshape, _x[i]); M[2].emplace_back(Mshape, _M[i]); } } else { const std::size_t num_ent = cell::num_sub_entities(celltype, 2); x[2] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[2] = std::vector(num_ent, impl::mdarray_t(0, 1, 0, 1)); } } if (tdim == 3) { if (degree >= 6) { auto [_x, xshape, _M, Mshape] = moments::make_integral_moments( element::create_dpc(cell::type::hexahedron, degree - 6, dvariant, true), celltype, polyset::type::standard, 1, 2 * degree - 6); assert(_x.size() == _M.size()); for (std::size_t i = 0; i < _x.size(); ++i) { x[3].emplace_back(xshape, _x[i]); M[3].emplace_back(Mshape, _M[i]); } } else { const std::size_t num_ent = cell::num_sub_entities(celltype, 3); x[3] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[3] = std::vector(num_ent, impl::mdarray_t(0, 1, 0, 1)); } } std::vector wbuffer; std::array wshape; if (tdim == 1) { wbuffer = math::eye(degree + 1); wshape = {static_cast(degree + 1), static_cast(degree + 1)}; } else if (tdim == 2) { auto w = make_serendipity_space_2d(degree); wbuffer.assign(w.data(), w.data() + w.size()); wshape = {w.extent(0), w.extent(1)}; } else if (tdim == 3) { auto w = make_serendipity_space_3d(degree); wbuffer.assign(w.data(), w.data() + w.size()); wshape = {w.extent(0), w.extent(1)}; } else { throw std::runtime_error("Unsupported tdim"); } std::array>, 4> xview = impl::to_mdspan(x); std::array>, 4> Mview = impl::to_mdspan(M); std::array>, 4> xbuffer; std::array>, 4> Mbuffer; if (discontinuous) { std::array>, 4> xshape; std::array>, 4> Mshape; std::tie(xbuffer, xshape, Mbuffer, Mshape) = element::make_discontinuous(xview, Mview, tdim, 1); xview = impl::to_mdspan(xbuffer, xshape); Mview = impl::to_mdspan(Mbuffer, Mshape); } sobolev::space space = discontinuous ? sobolev::space::L2 : sobolev::space::H1; return FiniteElement( element::family::serendipity, celltype, polyset::type::standard, degree, {}, impl::mdspan_t(wbuffer.data(), wshape), xview, Mview, 0, maps::type::identity, space, discontinuous, degree < static_cast(tdim) ? 1 : degree / tdim, degree, lvariant, dvariant); } //---------------------------------------------------------------------------- template FiniteElement element::create_dpc(cell::type celltype, int degree, element::dpc_variant variant, bool discontinuous) { // Only tabulate for scalar. Vector spaces can easily be built from // the scalar space. if (!discontinuous) throw std::runtime_error("Cannot create a continuous DPC element."); if (variant == element::dpc_variant::unset) { if (degree == 0) variant = element::dpc_variant::simplex_equispaced; else { throw std::runtime_error( "DPC elements of degree > 0 need to be given a variant."); } } cell::type simplex_type; switch (celltype) { case cell::type::quadrilateral: simplex_type = cell::type::triangle; break; case cell::type::hexahedron: simplex_type = cell::type::tetrahedron; break; default: throw std::runtime_error("Invalid cell type"); } if (variant == element::dpc_variant::legendre) return create_legendre_dpc(celltype, degree, discontinuous); const std::size_t ndofs = polyset::dim(simplex_type, polyset::type::standard, degree); const std::size_t psize = polyset::dim(celltype, polyset::type::standard, degree); impl::mdarray_t wcoeffs(ndofs, psize); if (celltype == cell::type::quadrilateral) { int row_n = 0; for (int i = 0; i <= degree; ++i) for (int j = 0; j <= degree - i; ++j) wcoeffs(row_n++, i * (degree + 1) + j) = 1; } else { int row_n = 0; for (int i = 0; i <= degree; ++i) { for (int j = 0; j <= degree - i; ++j) { for (int k = 0; k <= degree - i - j; ++k) { wcoeffs(row_n++, i * (degree + 1) * (degree + 1) + j * (degree + 1) + k) = 1; } } } } std::array>, 4> x; std::array>, 4> M; const std::size_t tdim = cell::topological_dimension(celltype); for (std::size_t i = 0; i < tdim; ++i) { const std::size_t num_ent = cell::num_sub_entities(celltype, i); x[i] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[i] = std::vector(num_ent, impl::mdarray_t(0, 1, 0, 1)); } auto& _M = M[tdim].emplace_back(std::array{ndofs, 1, ndofs, 1}, 0); for (std::size_t i = 0; i < _M.extent(0); ++i) _M(i, 0, i, 0) = 1.0; impl::mdarray_t pt = make_dpc_points(celltype, degree, variant); x[tdim].push_back(pt); return FiniteElement( element::family::DPC, celltype, polyset::type::standard, degree, {}, impl::mdspan_t(wcoeffs.data(), wcoeffs.extents()), impl::to_mdspan(x), impl::to_mdspan(M), 0, maps::type::identity, sobolev::space::L2, discontinuous, degree, degree, element::lagrange_variant::unset, variant); } //----------------------------------------------------------------------------- template FiniteElement element::create_serendipity_div( cell::type celltype, int degree, element::lagrange_variant lvariant, element::dpc_variant dvariant, bool discontinuous) { if (degree == 0) throw std::runtime_error("Cannot create degree 0 serendipity"); if (celltype != cell::type::quadrilateral and celltype != cell::type::hexahedron) { throw std::runtime_error("Invalid celltype"); } const std::size_t tdim = cell::topological_dimension(celltype); const cell::type facettype = (tdim == 2) ? cell::type::interval : cell::type::quadrilateral; std::array>, 4> x; std::array>, 4> M; for (std::size_t i = 0; i < tdim - 1; ++i) { const std::size_t num_ent = cell::num_sub_entities(celltype, i); x[i] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[i] = std::vector(num_ent, impl::mdarray_t(0, tdim, 0, 1)); } { FiniteElement facet_moment_space = facettype == cell::type::interval ? element::create_lagrange(facettype, degree, lvariant, true) : element::create_dpc(facettype, degree, dvariant, true); auto [_x, xshape, _M, Mshape] = moments::make_normal_integral_moments( facet_moment_space, celltype, polyset::type::standard, tdim, 2 * degree + 1); assert(_x.size() == _M.size()); for (std::size_t i = 0; i < _x.size(); ++i) { x[tdim - 1].emplace_back(xshape, _x[i]); M[tdim - 1].emplace_back(Mshape, _M[i]); } } if (degree >= 2) { FiniteElement cell_moment_space = element::create_dpc(celltype, degree - 2, dvariant, true); auto [_x, xshape, _M, Mshape] = moments::make_integral_moments( cell_moment_space, celltype, polyset::type::standard, tdim, 2 * degree - 1); assert(_x.size() == _M.size()); for (std::size_t i = 0; i < _x.size(); ++i) { x[tdim].emplace_back(xshape, _x[i]); M[tdim].emplace_back(Mshape, _M[i]); } } else { const std::size_t num_ent = cell::num_sub_entities(celltype, tdim); x[tdim] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[tdim] = std::vector(num_ent, impl::mdarray_t(0, tdim, 0, 1)); } std::vector wbuffer; std::array wshape; if (tdim == 2) { auto w = make_serendipity_div_space_2d(degree); wbuffer.assign(w.data(), w.data() + w.size()); wshape = {w.extent(0), w.extent(1)}; } else if (tdim == 3) { auto w = make_serendipity_div_space_3d(degree); wbuffer.assign(w.data(), w.data() + w.size()); wshape = {w.extent(0), w.extent(1)}; } else throw std::runtime_error("Unsupported tdim"); std::array>, 4> xview = impl::to_mdspan(x); std::array>, 4> Mview = impl::to_mdspan(M); std::array>, 4> xbuffer; std::array>, 4> Mbuffer; if (discontinuous) { std::array>, 4> xshape; std::array>, 4> Mshape; std::tie(xbuffer, xshape, Mbuffer, Mshape) = element::make_discontinuous(xview, Mview, tdim, tdim); xview = impl::to_mdspan(xbuffer, xshape); Mview = impl::to_mdspan(Mbuffer, Mshape); } sobolev::space space = discontinuous ? sobolev::space::L2 : sobolev::space::HDiv; return FiniteElement( element::family::BDM, celltype, polyset::type::standard, degree, {tdim}, impl::mdspan_t(wbuffer.data(), wshape), xview, Mview, 0, maps::type::contravariantPiola, space, discontinuous, degree / tdim, degree + 1, lvariant, dvariant); } //----------------------------------------------------------------------------- template FiniteElement element::create_serendipity_curl( cell::type celltype, int degree, element::lagrange_variant lvariant, element::dpc_variant dvariant, bool discontinuous) { if (degree == 0) throw std::runtime_error("Cannot create degree 0 serendipity"); if (celltype != cell::type::quadrilateral and celltype != cell::type::hexahedron) { throw std::runtime_error("Invalid celltype"); } const std::size_t tdim = cell::topological_dimension(celltype); // Evaluate the expansion polynomials at the quadrature points const auto [_Qpts, wts] = quadrature::make_quadrature(quadrature::type::Default, celltype, polyset::type::standard, 2 * degree + 1); impl::mdspan_t Qpts(_Qpts.data(), wts.size(), _Qpts.size() / wts.size()); std::vector wbuffer; std::array wshape; if (tdim == 2) { auto w = make_serendipity_curl_space_2d(degree); wbuffer.assign(w.data(), w.data() + w.size()); wshape = {w.extent(0), w.extent(1)}; } else if (tdim == 3) { auto w = make_serendipity_curl_space_3d(degree); wbuffer.assign(w.data(), w.data() + w.size()); wshape = {w.extent(0), w.extent(1)}; } else { throw std::runtime_error("Unsupported tdim"); } std::array>, 4> x; std::array>, 4> M; { const std::size_t num_ent = cell::num_sub_entities(celltype, 0); x[0] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[0] = std::vector(num_ent, impl::mdarray_t(0, tdim, 0, 1)); } { FiniteElement edge_moment_space = element::create_lagrange( cell::type::interval, degree, lvariant, true); auto [_x, xshape, _M, Mshape] = moments::make_tangent_integral_moments( edge_moment_space, celltype, polyset::type::standard, tdim, 2 * degree + 1); assert(_x.size() == _M.size()); for (std::size_t i = 0; i < _x.size(); ++i) { x[1].emplace_back(xshape, _x[i]); M[1].emplace_back(Mshape, _M[i]); } } if (degree >= 2) { // Face integral moment FiniteElement moment_space = element::create_dpc( cell::type::quadrilateral, degree - 2, dvariant, true); auto [_x, xshape, _M, Mshape] = moments::make_integral_moments( moment_space, celltype, polyset::type::standard, tdim, 2 * degree - 1); assert(_x.size() == _M.size()); for (std::size_t i = 0; i < _x.size(); ++i) { x[2].emplace_back(xshape, _x[i]); M[2].emplace_back(Mshape, _M[i]); } } else { const std::size_t num_ent = cell::num_sub_entities(celltype, 2); x[2] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[2] = std::vector(num_ent, impl::mdarray_t(0, tdim, 0, 1)); } if (tdim == 3) { if (degree >= 4) { // Interior integral moment auto [_x, xshape, _M, Mshape] = moments::make_integral_moments( element::create_dpc(cell::type::hexahedron, degree - 4, dvariant, true), celltype, polyset::type::standard, tdim, 2 * degree - 3); assert(_x.size() == _M.size()); for (std::size_t i = 0; i < _x.size(); ++i) { x[3].emplace_back(xshape, _x[i]); M[3].emplace_back(Mshape, _M[i]); } } else { const std::size_t num_ent = cell::num_sub_entities(celltype, 3); x[3] = std::vector(num_ent, impl::mdarray_t(0, tdim)); M[3] = std::vector(num_ent, impl::mdarray_t(0, tdim, 0, 1)); } } std::array>, 4> xview = impl::to_mdspan(x); std::array>, 4> Mview = impl::to_mdspan(M); std::array>, 4> xbuffer; std::array>, 4> Mbuffer; if (discontinuous) { std::array>, 4> xshape; std::array>, 4> Mshape; std::tie(xbuffer, xshape, Mbuffer, Mshape) = element::make_discontinuous(xview, Mview, tdim, tdim); xview = impl::to_mdspan(xbuffer, xshape); Mview = impl::to_mdspan(Mbuffer, Mshape); } sobolev::space space = discontinuous ? sobolev::space::L2 : sobolev::space::HCurl; return FiniteElement( element::family::N2E, celltype, polyset::type::standard, degree, {tdim}, impl::mdspan_t(wbuffer.data(), wshape), xview, Mview, 0, maps::type::covariantPiola, space, discontinuous, (degree == 2 && tdim == 3) ? 1 : degree / tdim, degree + 1, lvariant, dvariant); } //----------------------------------------------------------------------------- template FiniteElement element::create_serendipity(cell::type, int, element::lagrange_variant, element::dpc_variant, bool); template FiniteElement element::create_serendipity(cell::type, int, element::lagrange_variant, element::dpc_variant, bool); template FiniteElement element::create_dpc(cell::type, int, element::dpc_variant, bool); template FiniteElement element::create_dpc(cell::type, int, element::dpc_variant, bool); template FiniteElement element::create_serendipity_div(cell::type, int, element::lagrange_variant, element::dpc_variant, bool); template FiniteElement element::create_serendipity_div(cell::type, int, element::lagrange_variant, element::dpc_variant, bool); template FiniteElement element::create_serendipity_curl(cell::type, int, element::lagrange_variant, element::dpc_variant, bool); template FiniteElement element::create_serendipity_curl(cell::type, int, element::lagrange_variant, element::dpc_variant, bool); //----------------------------------------------------------------------------- fenics-basix-0.9.0/cpp/basix/e-serendipity.h000066400000000000000000000063501470517546000207320ustar00rootroot00000000000000// Copyright (c) 2020 Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #pragma once #include "cell.h" #include "element-families.h" #include "finite-element.h" #include namespace basix::element { /// Create a serendipity element on cell with given degree /// @param[in] celltype The cell type /// @param[in] degree The degree of the element /// @param[in] lvariant The variant of the Lagrange element to be used for /// integral moments on the edges of the cell /// @param[in] dvariant The variant of the DPC element to be used for /// integral moments on the interior of the cell (for quads and hexes). For /// elements on an interval element::dpc_variant::unset can be passed in /// @param[in] discontinuous Controls whether the element is continuous or /// discontinuous /// @return A finite element template FiniteElement create_serendipity(cell::type celltype, int degree, element::lagrange_variant lvariant, element::dpc_variant dvariant, bool discontinuous); /// Create a DPC (discontinuous polynomial cubical) element on cell with given /// degree. /// @note DPC elements must be discontinuous /// @param[in] celltype The cell type /// @param[in] degree The degree of the element /// @param[in] variant The variant of the element to be created /// @param[in] discontinuous Controls whether the element is continuous or /// discontinuous /// @return A finite element template FiniteElement create_dpc(cell::type celltype, int degree, element::dpc_variant variant, bool discontinuous); /// Create a serendipity H(div) element on cell with given degree /// @param[in] celltype The cell type /// @param[in] degree The degree of the element /// @param[in] lvariant The variant of the Lagrange element to be used for /// integral moments /// @param[in] dvariant The variant of the DPC element to be used for /// integral moments /// @param[in] discontinuous Controls whether the element is continuous or /// discontinuous /// @return A finite element template FiniteElement create_serendipity_div(cell::type celltype, int degree, element::lagrange_variant lvariant, element::dpc_variant dvariant, bool discontinuous); /// Create a serendipity H(curl) element on cell with given degree /// @param[in] celltype The cell type /// @param[in] degree The degree of the element /// @param[in] lvariant The variant of the Lagrange element to be used for /// integral moments /// @param[in] dvariant The variant of the DPC element to be used for /// integral moments /// @param[in] discontinuous Controls whether the element is continuous or /// discontinuous /// @return A finite element template FiniteElement create_serendipity_curl(cell::type celltype, int degree, element::lagrange_variant lvariant, element::dpc_variant dvariant, bool discontinuous); } // namespace basix::element fenics-basix-0.9.0/cpp/basix/element-families.h000066400000000000000000000022331470517546000213650ustar00rootroot00000000000000// Copyright (c) 2020 Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #pragma once /// Interfaces for creating finite elements namespace basix::element { /// Variants of a Lagrange space that can be created enum class lagrange_variant { unset = 0, equispaced = 1, gll_warped = 2, gll_isaac = 3, gll_centroid = 4, chebyshev_warped = 5, chebyshev_isaac = 6, chebyshev_centroid = 7, gl_warped = 8, gl_isaac = 9, gl_centroid = 10, legendre = 11, bernstein = 12, }; /// Variants of a DPC (discontinuous polynomial cubical) space that can /// be created. DPC spaces span the same set of polynomials as Lagrange /// spaces on simplices but are defined on tensor product cells. enum class dpc_variant { unset = 0, simplex_equispaced = 1, simplex_gll = 2, horizontal_equispaced = 3, horizontal_gll = 4, diagonal_equispaced = 5, diagonal_gll = 6, legendre = 7, }; /// Available element families enum class family { custom = 0, P = 1, RT = 2, N1E = 3, BDM = 4, N2E = 5, CR = 6, Regge = 7, DPC = 8, bubble = 9, serendipity = 10, HHJ = 11, Hermite = 12, iso = 13, }; } // namespace basix::element fenics-basix-0.9.0/cpp/basix/finite-element.cpp000066400000000000000000002015121470517546000214060ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson & Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #include "finite-element.h" #include "dof-transformations.h" #include "e-brezzi-douglas-marini.h" #include "e-bubble.h" #include "e-crouzeix-raviart.h" #include "e-hermite.h" #include "e-hhj.h" #include "e-lagrange.h" #include "e-nce-rtc.h" #include "e-nedelec.h" #include "e-raviart-thomas.h" #include "e-regge.h" #include "e-serendipity.h" #include "math.h" #include "polyset.h" #include #include #include #include #include #include #define str_macro(X) #X #define str(X) str_macro(X) using namespace basix; namespace stdex = MDSPAN_IMPL_STANDARD_NAMESPACE::MDSPAN_IMPL_PROPOSED_NAMESPACE; template using mdspan_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>; template using mdarray_t = stdex::mdarray>; namespace { //---------------------------------------------------------------------------- constexpr int compute_value_size(maps::type map_type, int dim) { switch (map_type) { case maps::type::identity: return 1; case maps::type::covariantPiola: return dim; case maps::type::contravariantPiola: return dim; case maps::type::doubleCovariantPiola: return dim * dim; case maps::type::doubleContravariantPiola: return dim * dim; default: throw std::runtime_error("Mapping not yet implemented"); } } //----------------------------------------------------------------------------- constexpr int num_transformations(cell::type cell_type) { switch (cell_type) { case cell::type::point: return 0; case cell::type::interval: return 0; case cell::type::triangle: return 3; case cell::type::quadrilateral: return 4; case cell::type::tetrahedron: return 14; case cell::type::hexahedron: return 24; case cell::type::prism: return 19; case cell::type::pyramid: return 18; default: throw std::runtime_error("Cell type not yet supported"); } } //----------------------------------------------------------------------------- template std::pair, std::array> compute_dual_matrix( cell::type cell_type, polyset::type poly_type, mdspan_t B, const std::array>, 4>& x, const std::array>, 4>& M, int degree, int nderivs) { std::size_t num_dofs(0), vs(0); for (auto& Md : M) { for (auto& Me : Md) { num_dofs += Me.extent(0); if (vs == 0) vs = Me.extent(1); else if (vs != Me.extent(1)) throw std::runtime_error("Inconsistent value size"); } } std::size_t pdim = polyset::dim(cell_type, poly_type, degree); mdarray_t D(vs, pdim, num_dofs); std::fill(D.data(), D.data() + D.size(), 0); std::vector Pb; // Loop over different dimensions std::size_t dof_index = 0; for (std::size_t d = 0; d < M.size(); ++d) { // Loop over entities of dimension d for (std::size_t e = 0; e < x[d].size(); ++e) { // Evaluate polynomial basis at x[d] mdspan_t x_e = x[d][e]; mdspan_t P; if (x_e.extent(0) > 0) { std::array shape; std::tie(Pb, shape) = polyset::tabulate(cell_type, poly_type, degree, nderivs, x_e); P = mdspan_t(Pb.data(), shape); } // Me: [dof, vs, point, deriv] mdspan_t Me = M[d][e]; // Compute dual matrix contribution if (Me.extent(3) > 1) { for (std::size_t l = 0; l < Me.extent(3); ++l) // Derivative for (std::size_t m = 0; m < P.extent(1); ++m) // Polynomial term for (std::size_t i = 0; i < Me.extent(0); ++i) // Dof index for (std::size_t j = 0; j < Me.extent(1); ++j) // Value index for (std::size_t k = 0; k < Me.extent(2); ++k) // Point D(j, m, dof_index + i) += Me(i, j, k, l) * P(l, m, k); } else { // Flatten and use matrix-matrix multiplication, possibly using // BLAS for larger cases. We can do this straightforwardly when // Me.extent(3) == 1 since we are contracting over one index // only. std::vector Pt_b(P.extent(2) * P.extent(1)); mdspan_t Pt(Pt_b.data(), P.extent(2), P.extent(1)); for (std::size_t i = 0; i < Pt.extent(0); ++i) for (std::size_t j = 0; j < Pt.extent(1); ++j) Pt(i, j) = P(0, j, i); std::vector De_b(Me.extent(0) * Me.extent(1) * Pt.extent(1)); mdspan_t De(De_b.data(), Me.extent(0) * Me.extent(1), Pt.extent(1)); math::dot(mdspan_t(Me.data_handle(), Me.extent(0) * Me.extent(1), Me.extent(2)), Pt, De); // Expand and copy for (std::size_t i = 0; i < Me.extent(0); ++i) for (std::size_t j = 0; j < Me.extent(1); ++j) for (std::size_t k = 0; k < P.extent(1); ++k) D(j, k, dof_index + i) += De(i * Me.extent(1) + j, k); } dof_index += M[d][e].extent(0); } } // Flatten D mdspan_t Df(D.data(), D.extent(0) * D.extent(1), D.extent(2)); std::array shape = {B.extent(0), Df.extent(1)}; std::vector C(shape[0] * shape[1]); math::dot(B, Df, mdspan_t(C.data(), shape)); return {std::move(C), shape}; } //----------------------------------------------------------------------------- void combine_hashes(std::size_t& a, std::size_t b) { a ^= b + 0x9e3779b9 + (a << 6) + (a >> 2); } //----------------------------------------------------------------------------- } // namespace //----------------------------------------------------------------------------- template FiniteElement basix::create_element(element::family family, cell::type cell, int degree, element::lagrange_variant lvariant, element::dpc_variant dvariant, bool discontinuous, std::vector dof_ordering) { if (family == element::family::custom) { throw std::runtime_error("Cannot create a custom element directly. Try " "using `create_custom_element` instead"); } if (degree < 0) { throw std::runtime_error("Cannot create an element with a negative degree"); } // Checklist of variant compatibility (lagrange, DPC) for each static const std::map> has_variant = {{element::family::P, {true, false}}, {element::family::RT, {true, false}}, {element::family::N1E, {true, false}}, {element::family::serendipity, {true, true}}, {element::family::DPC, {false, true}}, {element::family::Regge, {false, false}}, {element::family::HHJ, {false, false}}, {element::family::CR, {false, false}}, {element::family::bubble, {false, false}}, {element::family::Hermite, {false, false}}, {element::family::iso, {true, false}}}; if (auto it = has_variant.find(family); it != has_variant.end()) { if (it->second[0] == false and lvariant != element::lagrange_variant::unset) { throw std::runtime_error( "Cannot pass a Lagrange variant to this element."); } if (it->second[1] == false and dvariant != element::dpc_variant::unset) throw std::runtime_error("Cannot pass a DPC variant to this element."); } if (!dof_ordering.empty() and family != element::family::P) { throw std::runtime_error("DOF ordering only supported for Lagrange"); } switch (family) { // P family case element::family::P: return element::create_lagrange(cell, degree, lvariant, discontinuous, dof_ordering); case element::family::RT: { switch (cell) { case cell::type::quadrilateral: return element::create_rtc(cell, degree, lvariant, discontinuous); case cell::type::hexahedron: return element::create_rtc(cell, degree, lvariant, discontinuous); default: return element::create_rt(cell, degree, lvariant, discontinuous); } } case element::family::N1E: { switch (cell) { case cell::type::quadrilateral: return element::create_nce(cell, degree, lvariant, discontinuous); case cell::type::hexahedron: return element::create_nce(cell, degree, lvariant, discontinuous); default: return element::create_nedelec(cell, degree, lvariant, discontinuous); } } // S family case element::family::serendipity: return element::create_serendipity(cell, degree, lvariant, dvariant, discontinuous); case element::family::BDM: switch (cell) { case cell::type::quadrilateral: return element::create_serendipity_div(cell, degree, lvariant, dvariant, discontinuous); case cell::type::hexahedron: return element::create_serendipity_div(cell, degree, lvariant, dvariant, discontinuous); default: return element::create_bdm(cell, degree, lvariant, discontinuous); } case element::family::N2E: switch (cell) { case cell::type::quadrilateral: return element::create_serendipity_curl(cell, degree, lvariant, dvariant, discontinuous); case cell::type::hexahedron: return element::create_serendipity_curl(cell, degree, lvariant, dvariant, discontinuous); default: return element::create_nedelec2(cell, degree, lvariant, discontinuous); } case element::family::DPC: return element::create_dpc(cell, degree, dvariant, discontinuous); // Matrix elements case element::family::Regge: return element::create_regge(cell, degree, discontinuous); case element::family::HHJ: return element::create_hhj(cell, degree, discontinuous); // Other elements case element::family::CR: return element::create_cr(cell, degree, discontinuous); case element::family::bubble: return element::create_bubble(cell, degree, discontinuous); case element::family::iso: return element::create_iso(cell, degree, lvariant, discontinuous); case element::family::Hermite: return element::create_hermite(cell, degree, discontinuous); default: throw std::runtime_error("Element family not found."); } } //----------------------------------------------------------------------------- template basix::FiniteElement basix::create_element(element::family, cell::type, int, element::lagrange_variant, element::dpc_variant, bool, std::vector); template basix::FiniteElement basix::create_element(element::family, cell::type, int, element::lagrange_variant, element::dpc_variant, bool, std::vector); //----------------------------------------------------------------------------- template FiniteElement basix::create_tp_element(element::family family, cell::type cell, int degree, element::lagrange_variant lvariant, element::dpc_variant dvariant, bool discontinuous) { std::vector dof_ordering = tp_dof_ordering( family, cell, degree, lvariant, dvariant, discontinuous); return create_element(family, cell, degree, lvariant, dvariant, discontinuous, dof_ordering); } //----------------------------------------------------------------------------- template basix::FiniteElement basix::create_tp_element(element::family, cell::type, int, element::lagrange_variant, element::dpc_variant, bool); template basix::FiniteElement basix::create_tp_element(element::family, cell::type, int, element::lagrange_variant, element::dpc_variant, bool); //----------------------------------------------------------------------------- template std::vector>> basix::tp_factors(element::family family, cell::type cell, int degree, element::lagrange_variant lvariant, element::dpc_variant dvariant, bool discontinuous, std::vector dof_ordering) { std::vector tp_dofs = tp_dof_ordering(family, cell, degree, lvariant, dvariant, discontinuous); if (!tp_dofs.empty() && tp_dofs == dof_ordering) { switch (family) { case element::family::P: { FiniteElement sub_element = create_element(element::family::P, cell::type::interval, degree, lvariant, dvariant, true); switch (cell) { case cell::type::quadrilateral: { return {{sub_element, sub_element}}; } case cell::type::hexahedron: { return {{sub_element, sub_element, sub_element}}; } default: { throw std::runtime_error("Invalid celltype."); } } break; } default: { throw std::runtime_error("Invalid family."); } } } throw std::runtime_error( "Element does not have tensor product factorisation."); } //----------------------------------------------------------------------------- template std::vector>> basix::tp_factors(element::family, cell::type, int, element::lagrange_variant, element::dpc_variant, bool, std::vector); template std::vector>> basix::tp_factors(element::family, cell::type, int, element::lagrange_variant, element::dpc_variant, bool, std::vector); //----------------------------------------------------------------------------- std::vector basix::tp_dof_ordering(element::family family, cell::type cell, int degree, element::lagrange_variant, element::dpc_variant, bool) { std::vector dof_ordering; std::vector perm; switch (family) { case element::family::P: { switch (cell) { case cell::type::quadrilateral: { perm.push_back(0); if (degree > 0) { int n = degree - 1; perm.push_back(2); for (int i = 0; i < n; ++i) perm.push_back(4 + n + i); perm.push_back(1); perm.push_back(3); for (int i = 0; i < n; ++i) perm.push_back(4 + 2 * n + i); for (int i = 0; i < n; ++i) { perm.push_back(4 + i); perm.push_back(4 + 3 * n + i); for (int j = 0; j < n; ++j) perm.push_back(4 + i + (4 + j) * n); } } assert((int)perm.size() == (degree + 1) * (degree + 1)); break; } case cell::type::hexahedron: { perm.push_back(0); if (degree > 0) { int n = degree - 1; perm.push_back(4); for (int i = 0; i < n; ++i) perm.push_back(8 + 2 * n + i); perm.push_back(2); perm.push_back(6); for (int i = 0; i < n; ++i) perm.push_back(8 + 6 * n + i); for (int i = 0; i < n; ++i) { perm.push_back(8 + n + i); perm.push_back(8 + 9 * n + i); for (int j = 0; j < n; ++j) perm.push_back(8 + 12 * n + 2 * n * n + i + n * j); } perm.push_back(1); perm.push_back(5); for (int i = 0; i < n; ++i) perm.push_back(8 + 4 * n + i); perm.push_back(3); perm.push_back(7); for (int i = 0; i < n; ++i) perm.push_back(8 + 7 * n + i); for (int i = 0; i < n; ++i) { perm.push_back(8 + 3 * n + i); perm.push_back(8 + 10 * n + i); for (int j = 0; j < n; ++j) perm.push_back(8 + 12 * n + 3 * n * n + i + n * j); } for (int i = 0; i < n; ++i) { perm.push_back(8 + i); perm.push_back(8 + 8 * n + i); for (int j = 0; j < n; ++j) perm.push_back(8 + 12 * n + n * n + i + n * j); perm.push_back(8 + 5 * n + i); perm.push_back(8 + 11 * n + i); for (int j = 0; j < n; ++j) perm.push_back(8 + 12 * n + 4 * n * n + i + n * j); for (int j = 0; j < n; ++j) { perm.push_back(8 + 12 * n + i + n * j); perm.push_back(8 + 12 * n + 5 * n * n + i + n * j); for (int k = 0; k < n; ++k) perm.push_back(8 + 12 * n + 6 * n * n + i + n * j + n * n * k); } } } assert((int)perm.size() == (degree + 1) * (degree + 1) * (degree + 1)); break; } default: { } } break; } default: { } } if (perm.size() == 0) { throw std::runtime_error( "Element does not have tensor product factorisation."); } dof_ordering.resize(perm.size()); for (std::size_t i = 0; i < perm.size(); ++i) dof_ordering[perm[i]] = i; return dof_ordering; } //----------------------------------------------------------------------------- template std::tuple>, 4>, std::array>, 4>, std::array>, 4>, std::array>, 4>> element::make_discontinuous( const std::array>, 4>& x, const std::array>, 4>& M, std::size_t tdim, std::size_t value_size) { std::size_t npoints = 0; std::size_t Mshape0 = 0; for (int i = 0; i < 4; ++i) { for (std::size_t j = 0; j < x[i].size(); ++j) { npoints += x[i][j].extent(0); Mshape0 += M[i][j].extent(0); } } const std::size_t nderivs = M[0][0].extent(3); std::array>, 4> x_data; std::array>, 4> xshapes; std::array>, 4> M_data; std::array>, 4> Mshapes; for (std::size_t i = 0; i < tdim; ++i) { xshapes[i] = std::vector(x[i].size(), std::array{0, tdim}); x_data[i].resize(x[i].size()); Mshapes[i] = std::vector( M[i].size(), std::array{0, value_size, 0, nderivs}); M_data[i].resize(M[i].size()); } std::array xshape = {npoints, tdim}; std::vector xb(xshape[0] * xshape[1]); MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> new_x(xb.data(), xshape); std::array Mshape = {Mshape0, value_size, npoints, nderivs}; std::vector Mb(Mshape[0] * Mshape[1] * Mshape[2] * Mshape[3]); MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> new_M(Mb.data(), Mshape); int x_n = 0; int M_n = 0; for (int i = 0; i < 4; ++i) { for (std::size_t j = 0; j < x[i].size(); ++j) { for (std::size_t k0 = 0; k0 < x[i][j].extent(0); ++k0) for (std::size_t k1 = 0; k1 < x[i][j].extent(1); ++k1) new_x(k0 + x_n, k1) = x[i][j](k0, k1); for (std::size_t k0 = 0; k0 < M[i][j].extent(0); ++k0) for (std::size_t k1 = 0; k1 < M[i][j].extent(1); ++k1) for (std::size_t k2 = 0; k2 < M[i][j].extent(2); ++k2) for (std::size_t k3 = 0; k3 < M[i][j].extent(3); ++k3) new_M(k0 + M_n, k1, k2 + x_n, k3) = M[i][j](k0, k1, k2, k3); x_n += x[i][j].extent(0); M_n += M[i][j].extent(0); } } x_data[tdim].push_back(xb); xshapes[tdim].push_back(xshape); M_data[tdim].push_back(Mb); Mshapes[tdim].push_back(Mshape); return {std::move(x_data), std::move(xshapes), std::move(M_data), std::move(Mshapes)}; } //----------------------------------------------------------------------------- /// @cond template std::tuple>, 4>, std::array>, 4>, std::array>, 4>, std::array>, 4>> element::make_discontinuous( const std::array>, 4>&, const std::array>, 4>&, std::size_t, std::size_t); template std::tuple>, 4>, std::array>, 4>, std::array>, 4>, std::array>, 4>> element::make_discontinuous( const std::array>, 4>&, const std::array>, 4>&, std::size_t, std::size_t); /// @endcond //----------------------------------------------------------------------------- template FiniteElement basix::create_custom_element( cell::type cell_type, const std::vector& value_shape, impl::mdspan_t wcoeffs, const std::array>, 4>& x, const std::array>, 4>& M, int interpolation_nderivs, maps::type map_type, sobolev::space sobolev_space, bool discontinuous, int embedded_subdegree, int embedded_superdegree, polyset::type poly_type) { // Check that inputs are valid const std::size_t psize = polyset::dim(cell_type, poly_type, embedded_superdegree); const std::size_t value_size = std::reduce( value_shape.begin(), value_shape.end(), 1, std::multiplies{}); const std::size_t deriv_count = polyset::nderivs(cell_type, interpolation_nderivs); const std::size_t tdim = cell::topological_dimension(cell_type); std::size_t ndofs = 0; for (std::size_t i = 0; i <= 3; ++i) for (std::size_t j = 0; j < M[i].size(); ++j) ndofs += M[i][j].extent(0); // Check that wcoeffs have the correct shape if (wcoeffs.extent(1) != psize * value_size) throw std::runtime_error("wcoeffs has the wrong number of columns"); if (wcoeffs.extent(0) != ndofs) throw std::runtime_error("wcoeffs has the wrong number of rows"); std::vector wcoeffs_ortho_b(wcoeffs.extent(0) * wcoeffs.extent(1)); { // scope mdspan_t wcoeffs_ortho(wcoeffs_ortho_b.data(), wcoeffs.extent(0), wcoeffs.extent(1)); std::copy(wcoeffs.data_handle(), wcoeffs.data_handle() + wcoeffs.size(), wcoeffs_ortho_b.begin()); basix::math::orthogonalise(wcoeffs_ortho); } mdspan_t wcoeffs_ortho(wcoeffs_ortho_b.data(), wcoeffs.extent(0), wcoeffs.extent(1)); // Check that x has the right shape for (std::size_t i = 0; i < x.size(); ++i) { if (x[i].size() != (i > tdim ? 0 : static_cast( cell::num_sub_entities(cell_type, i)))) { throw std::runtime_error("x has the wrong number of entities"); } for (const auto& xj : x[i]) { if (xj.extent(1) != tdim) throw std::runtime_error("x has a point with the wrong tdim"); } } // Check that M has the right shape for (std::size_t i = 0; i < M.size(); ++i) { if (M[i].size() != (i > tdim ? 0 : static_cast( cell::num_sub_entities(cell_type, i)))) throw std::runtime_error("M has the wrong number of entities"); for (std::size_t j = 0; j < M[i].size(); ++j) { if (M[i][j].extent(2) != x[i][j].extent(0)) { throw std::runtime_error( "M has the wrong shape (dimension 2 is wrong)"); } if (M[i][j].extent(1) != value_size) { throw std::runtime_error( "M has the wrong shape (dimension 1 is wrong)"); } if (M[i][j].extent(3) != deriv_count) { throw std::runtime_error( "M has the wrong shape (dimension 3 is wrong)"); } } } auto [dualmatrix, dualshape] = compute_dual_matrix(cell_type, poly_type, wcoeffs_ortho, x, M, embedded_superdegree, interpolation_nderivs); if (math::is_singular(mdspan_t(dualmatrix.data(), dualshape))) { throw std::runtime_error( "Dual matrix is singular, there is an error in your inputs"); } return basix::FiniteElement( element::family::custom, cell_type, poly_type, embedded_superdegree, value_shape, wcoeffs_ortho, x, M, interpolation_nderivs, map_type, sobolev_space, discontinuous, embedded_subdegree, embedded_superdegree, element::lagrange_variant::unset, element::dpc_variant::unset); } //----------------------------------------------------------------------------- /// @cond template FiniteElement basix::create_custom_element( cell::type, const std::vector&, impl::mdspan_t wcoeffs, const std::array>, 4>&, const std::array>, 4>&, int, maps::type, sobolev::space sobolev_space, bool, int, int, polyset::type); template FiniteElement basix::create_custom_element( cell::type, const std::vector&, impl::mdspan_t wcoeffs, const std::array>, 4>&, const std::array>, 4>&, int, maps::type, sobolev::space sobolev_space, bool, int, int, polyset::type); /// @endcond //----------------------------------------------------------------------------- /// @cond template FiniteElement::FiniteElement( element::family family, cell::type cell_type, polyset::type poly_type, int degree, const std::vector& value_shape, mdspan_t wcoeffs, const std::array>, 4>& x, const std::array>, 4>& M, int interpolation_nderivs, maps::type map_type, sobolev::space sobolev_space, bool discontinuous, int embedded_subdegree, int embedded_superdegree, element::lagrange_variant lvariant, element::dpc_variant dvariant, std::vector dof_ordering) : _cell_type(cell_type), _poly_type(poly_type), _cell_tdim(cell::topological_dimension(cell_type)), _cell_subentity_types(cell::subentity_types(cell_type)), _family(family), _lagrange_variant(lvariant), _dpc_variant(dvariant), _degree(degree), _interpolation_nderivs(interpolation_nderivs), _embedded_superdegree(embedded_superdegree), _embedded_subdegree(embedded_subdegree), _value_shape(value_shape), _map_type(map_type), _sobolev_space(sobolev_space), _discontinuous(discontinuous), _dof_ordering(dof_ordering) { // Check that discontinuous elements only have DOFs on interior if (discontinuous) { for (std::size_t i = 0; i < _cell_tdim; ++i) { for (auto& xi : x[i]) { if (xi.extent(0) > 0) { throw std::runtime_error( "Discontinuous element can only have interior DOFs."); } } } } try { _tensor_factors = tp_factors(family, cell_type, degree, lvariant, dvariant, discontinuous, dof_ordering); } catch (...) { } std::vector wcoeffs_b(wcoeffs.extent(0) * wcoeffs.extent(1)); std::copy(wcoeffs.data_handle(), wcoeffs.data_handle() + wcoeffs.size(), wcoeffs_b.begin()); _wcoeffs = {wcoeffs_b, {wcoeffs.extent(0), wcoeffs.extent(1)}}; _dual_matrix = compute_dual_matrix(cell_type, poly_type, wcoeffs, x, M, embedded_superdegree, interpolation_nderivs); // Copy x for (std::size_t i = 0; i < x.size(); ++i) { for (auto& xi : x[i]) { _x[i].emplace_back( std::vector(xi.data_handle(), xi.data_handle() + xi.size()), std::array{xi.extent(0), xi.extent(1)}); } } // Copy M for (std::size_t i = 0; i < M.size(); ++i) { for (auto Mi : M[i]) { _M[i].emplace_back( std::vector(Mi.data_handle(), Mi.data_handle() + Mi.size()), std::array{Mi.extent(0), Mi.extent(1), Mi.extent(2), Mi.extent(3)}); } } // Compute C = (BD^T)^{-1} B _coeffs.first = math::solve( mdspan_t(_dual_matrix.first.data(), _dual_matrix.second), wcoeffs); _coeffs.second = {_dual_matrix.second[1], wcoeffs.extent(1)}; std::size_t num_points = 0; for (auto& x_dim : x) for (auto& x_e : x_dim) num_points += x_e.extent(0); _points.first.reserve(num_points * _cell_tdim); _points.second = {num_points, _cell_tdim}; mdspan_t pview(_points.first.data(), _points.second); for (auto& x_dim : x) for (auto& x_e : x_dim) for (std::size_t p = 0; p < x_e.extent(0); ++p) for (std::size_t k = 0; k < x_e.extent(1); ++k) _points.first.push_back(x_e(p, k)); // Copy into _matM const std::size_t value_size = std::accumulate( value_shape.begin(), value_shape.end(), 1, std::multiplies{}); // Count number of dofs and point std::size_t num_dofs(0), num_points1(0); for (std::size_t d = 0; d < M.size(); ++d) { for (auto Me : M[d]) { num_dofs += Me.extent(0); num_points1 += Me.extent(2); } } // Check that number of dofs is equal to number of coefficients if (num_dofs != _coeffs.second[0]) { throw std::runtime_error( "Number of entity dofs does not match total number of dofs"); } _entity_transformations = doftransforms::compute_entity_transformations( cell_type, x, M, mdspan_t(_coeffs.first.data(), _coeffs.second), embedded_superdegree, value_size, map_type, poly_type); const std::size_t nderivs = polyset::nderivs(cell_type, interpolation_nderivs); _matM = {std::vector(num_dofs * value_size * num_points1 * nderivs), {num_dofs, value_size * num_points1 * nderivs}}; mdspan_t Mview(_matM.first.data(), num_dofs, value_size, num_points1, nderivs); // Loop over each topological dimensions std::size_t dof_offset(0), point_offset(0); for (std::size_t d = 0; d < M.size(); ++d) { // Loop of entities of dimension d for (auto& Me : M[d]) { for (std::size_t k0 = 0; k0 < Me.extent(0); ++k0) for (std::size_t k1 = 0; k1 < Mview.extent(1); ++k1) for (std::size_t k2 = 0; k2 < Me.extent(2); ++k2) for (std::size_t k3 = 0; k3 < Mview.extent(3); ++k3) Mview(k0 + dof_offset, k1, k2 + point_offset, k3) = Me(k0, k1, k2, k3); dof_offset += Me.extent(0); point_offset += Me.extent(2); } } // Compute number of dofs for each cell entity (computed from // interpolation data) int dof = 0; for (std::size_t d = 0; d < _cell_tdim + 1; ++d) { auto& edofs_d = _edofs.emplace_back(cell::num_sub_entities(_cell_type, d)); for (std::size_t e = 0; e < M[d].size(); ++e) for (std::size_t i = 0; i < M[d][e].extent(0); ++i) edofs_d[e].push_back(dof++); } if (!_dof_ordering.empty()) { const int ndof_order = _dof_ordering.size(); // Safety checks if (ndof_order != dof) throw std::runtime_error("Incorrect number of dofs in ordering."); std::vector check(_dof_ordering.size(), 0); for (int q : _dof_ordering) { if (q < 0 or q >= ndof_order) throw std::runtime_error("Out of range: dof_ordering."); check[q] += 1; } for (int q : check) if (q != 1) throw std::runtime_error("Dof ordering not a permutation."); // Apply permutation to _edofs for (std::size_t d = 0; d < _cell_tdim + 1; ++d) { for (auto& entity : _edofs[d]) { for (int& q : entity) q = _dof_ordering[q]; } } // Apply permutation to _points (for interpolation) std::vector new_points(_points.first.size()); assert(_points.second[0] == _dof_ordering.size()); const int gdim = _points.second[1]; for (std::size_t d = 0; d < _dof_ordering.size(); ++d) for (int i = 0; i < gdim; ++i) new_points[gdim * _dof_ordering[d] + i] = _points.first[gdim * d + i]; _points = {new_points, _points.second}; } const std::vector>>> connectivity = cell::sub_entity_connectivity(cell_type); for (std::size_t d = 0; d < _cell_tdim + 1; ++d) { auto& edofs_d = _e_closure_dofs.emplace_back(cell::num_sub_entities(_cell_type, d)); for (std::size_t e = 0; e < _e_closure_dofs[d].size(); ++e) { auto& closure_dofs = edofs_d[e]; for (std::size_t dim = 0; dim <= d; ++dim) { for (int c : connectivity[d][e][dim]) { closure_dofs.insert(closure_dofs.end(), _edofs[dim][c].begin(), _edofs[dim][c].end()); } } std::ranges::sort(_e_closure_dofs[d][e]); } } // Check if base transformations are all permutations _dof_transformations_are_permutations = true; _dof_transformations_are_identity = true; for (const auto& [ctype, trans_data] : _entity_transformations) { mdspan_t trans(trans_data.first.data(), trans_data.second); for (std::size_t i = 0; _dof_transformations_are_permutations and i < trans.extent(0); ++i) { for (std::size_t row = 0; row < trans.extent(1); ++row) { F rmin(0), rmax(0), rtot(0); for (std::size_t k = 0; k < trans.extent(2); ++k) { F r = trans(i, row, k); rmin = std::min(r, rmin); rmax = std::max(r, rmax); rtot += r; } constexpr F eps = 10.0 * std::numeric_limits::epsilon(); if ((trans.extent(2) != 1 and std::abs(rmin) > eps) or std::abs(rmax - 1.0) > eps or std::abs(rtot - 1.0) > eps) { _dof_transformations_are_permutations = false; _dof_transformations_are_identity = false; break; } if (std::abs(trans(i, row, row) - 1) > eps) _dof_transformations_are_identity = false; } } if (!_dof_transformations_are_permutations) break; } if (!_dof_transformations_are_identity) { // If transformations are permutations, then create the permutations if (_dof_transformations_are_permutations) { for (const auto& [ctype, trans_data] : _entity_transformations) { mdspan_t trans(trans_data.first.data(), trans_data.second); for (std::size_t i = 0; i < trans.extent(0); ++i) { std::vector perm(trans.extent(1)); std::vector inv_perm(trans.extent(1)); for (std::size_t row = 0; row < trans.extent(1); ++row) { for (std::size_t col = 0; col < trans.extent(1); ++col) { if (trans(i, row, col) > 0.5) { perm[row] = col; inv_perm[col] = row; break; } } } // Factorise the permutations precompute::prepare_permutation(perm); precompute::prepare_permutation(inv_perm); // Store the permutations auto& eperm = _eperm.try_emplace(ctype).first->second; auto& eperm_inv = _eperm_inv.try_emplace(ctype).first->second; eperm.push_back(perm); eperm_inv.push_back(inv_perm); // Generate the entity transformations from the permutations std::pair, std::array> identity = {std::vector(perm.size() * perm.size()), {perm.size(), perm.size()}}; std::ranges::fill(identity.first, 0.); for (std::size_t i = 0; i < perm.size(); ++i) identity.first[i * perm.size() + i] = 1; auto& etrans = _etrans.try_emplace(ctype).first->second; auto& etransT = _etransT.try_emplace(ctype).first->second; auto& etrans_invT = _etrans_invT.try_emplace(ctype).first->second; auto& etrans_inv = _etrans_inv.try_emplace(ctype).first->second; etrans.push_back({perm, identity}); etrans_invT.push_back({perm, identity}); etransT.push_back({inv_perm, identity}); etrans_inv.push_back({inv_perm, identity}); } } } else { // Precompute the DOF transformations for (const auto& [ctype, trans_data] : _entity_transformations) { mdspan_t trans(trans_data.first.data(), trans_data.second); // Buffers for matrices std::vector M_b, Minv_b, matint; auto& etrans = _etrans.try_emplace(ctype).first->second; auto& etransT = _etransT.try_emplace(ctype).first->second; auto& etrans_invT = _etrans_invT.try_emplace(ctype).first->second; auto& etrans_inv = _etrans_inv.try_emplace(ctype).first->second; for (std::size_t i = 0; i < trans.extent(0); ++i) { if (trans.extent(1) == 0) { etrans.push_back({}); etransT.push_back({}); etrans_invT.push_back({}); etrans_inv.push_back({}); } else { const std::size_t dim = trans.extent(1); assert(dim == trans.extent(2)); { std::pair, std::array> mat = {std::vector(dim * dim), {dim, dim}}; for (std::size_t k0 = 0; k0 < dim; ++k0) for (std::size_t k1 = 0; k1 < dim; ++k1) mat.first[k0 * dim + k1] = trans(i, k0, k1); std::vector mat_p = precompute::prepare_matrix(mat); etrans.push_back({mat_p, mat}); } { std::pair, std::array> matT = {std::vector(dim * dim), {dim, dim}}; for (std::size_t k0 = 0; k0 < dim; ++k0) for (std::size_t k1 = 0; k1 < dim; ++k1) matT.first[k0 * dim + k1] = trans(i, k1, k0); std::vector matT_p = precompute::prepare_matrix(matT); etransT.push_back({matT_p, matT}); } M_b.resize(dim * dim); mdspan_t M(M_b.data(), dim, dim); for (std::size_t k0 = 0; k0 < dim; ++k0) for (std::size_t k1 = 0; k1 < dim; ++k1) M(k0, k1) = trans(i, k0, k1); // Rotation of a face: this is in the only base transformation // such that M^{-1} != M. // For a quadrilateral face, M^4 = Id, so M^{-1} = M^3. // For a triangular face, M^3 = Id, so M^{-1} = M^2. Minv_b.resize(dim * dim); mdspan_t Minv(Minv_b.data(), dim, dim); if (ctype == cell::type::quadrilateral and i == 0) { matint.resize(dim * dim); mdspan_t mat_int(matint.data(), dim, dim); math::dot(M, M, mat_int); math::dot(mat_int, M, Minv); } else if (ctype == cell::type::triangle and i == 0) math::dot(M, M, Minv); else Minv_b.assign(M_b.begin(), M_b.end()); { std::pair, std::array> mat_inv = {std::vector(dim * dim), {dim, dim}}; for (std::size_t k0 = 0; k0 < dim; ++k0) for (std::size_t k1 = 0; k1 < dim; ++k1) mat_inv.first[k0 * dim + k1] = Minv(k0, k1); std::vector mat_inv_p = precompute::prepare_matrix(mat_inv); etrans_inv.push_back({mat_inv_p, mat_inv}); } { std::pair, std::array> mat_invT = {std::vector(dim * dim), {dim, dim}}; for (std::size_t k0 = 0; k0 < dim; ++k0) for (std::size_t k1 = 0; k1 < dim; ++k1) mat_invT.first[k0 * dim + k1] = Minv(k1, k0); std::vector mat_invT_p = precompute::prepare_matrix(mat_invT); etrans_invT.push_back({mat_invT_p, mat_invT}); } } } } } } // If DOF transformations are permutations, compute the subentity closure // permutations if (_dof_transformations_are_permutations) { if (_cell_tdim > 1) { // interval int dof_n = 0; const auto conn = cell::sub_entity_connectivity(_cell_type)[1][0]; std::vector>> dofs; dofs.resize(2); for (int dim = 0; dim <= 1; ++dim) { dofs[dim].resize(conn[dim].size()); for (std::size_t i = 0; i < conn[dim].size(); ++i) { const int e = conn[dim][i]; for (std::size_t j = 0; j < _edofs[dim][e].size(); ++j) { dofs[dim][i].push_back(dof_n++); } } } std::vector ref; ref.insert(ref.end(), dofs[0][1].begin(), dofs[0][1].end()); ref.insert(ref.end(), dofs[0][0].begin(), dofs[0][0].end()); // Edges ref.insert(ref.end(), dofs[1][0].begin(), dofs[1][0].end()); if (!_dof_transformations_are_identity) { auto& trans1 = _eperm.at(cell::type::interval)[0]; if (!dofs[1][0].empty()) { precompute::apply_permutation(trans1, std::span(ref), dofs[1][0][0]); } } precompute::prepare_permutation(ref); auto& secp = _subentity_closure_perm.try_emplace(cell::type::interval) .first->second; secp.push_back(ref); auto& secpi = _subentity_closure_perm_inv.try_emplace(cell::type::interval) .first->second; secpi.push_back(ref); } if (_cell_type == cell::type::tetrahedron || cell_type == cell::type::prism || cell_type == cell::type::pyramid) { // triangle const int face_n = cell_type == cell::type::pyramid ? 1 : 0; int dof_n = 0; const auto conn = cell::sub_entity_connectivity(_cell_type)[2][face_n]; std::vector>> dofs; dofs.resize(3); for (int dim = 0; dim <= 2; ++dim) { dofs[dim].resize(conn[dim].size()); for (std::size_t i = 0; i < conn[dim].size(); ++i) { const int e = conn[dim][i]; for (int j : _edofs[dim][e]) { std::ignore = j; dofs[dim][i].push_back(dof_n++); } } } std::vector rot; // Vertices rot.insert(rot.end(), dofs[0][1].begin(), dofs[0][1].end()); rot.insert(rot.end(), dofs[0][2].begin(), dofs[0][2].end()); rot.insert(rot.end(), dofs[0][0].begin(), dofs[0][0].end()); // Edges rot.insert(rot.end(), dofs[1][1].begin(), dofs[1][1].end()); rot.insert(rot.end(), dofs[1][2].begin(), dofs[1][2].end()); rot.insert(rot.end(), dofs[1][0].begin(), dofs[1][0].end()); // Face rot.insert(rot.end(), dofs[2][0].begin(), dofs[2][0].end()); std::vector rot_inv; // Vertices rot_inv.insert(rot_inv.end(), dofs[0][2].begin(), dofs[0][2].end()); rot_inv.insert(rot_inv.end(), dofs[0][0].begin(), dofs[0][0].end()); rot_inv.insert(rot_inv.end(), dofs[0][1].begin(), dofs[0][1].end()); // Edges rot_inv.insert(rot_inv.end(), dofs[1][2].begin(), dofs[1][2].end()); rot_inv.insert(rot_inv.end(), dofs[1][0].begin(), dofs[1][0].end()); rot_inv.insert(rot_inv.end(), dofs[1][1].begin(), dofs[1][1].end()); // Face rot_inv.insert(rot_inv.end(), dofs[2][0].begin(), dofs[2][0].end()); std::vector ref; // Vertices ref.insert(ref.end(), dofs[0][0].begin(), dofs[0][0].end()); ref.insert(ref.end(), dofs[0][2].begin(), dofs[0][2].end()); ref.insert(ref.end(), dofs[0][1].begin(), dofs[0][1].end()); // Edges ref.insert(ref.end(), dofs[1][0].begin(), dofs[1][0].end()); ref.insert(ref.end(), dofs[1][2].begin(), dofs[1][2].end()); ref.insert(ref.end(), dofs[1][1].begin(), dofs[1][1].end()); // Face ref.insert(ref.end(), dofs[2][0].begin(), dofs[2][0].end()); if (!_dof_transformations_are_identity) { auto& trans1 = _eperm.at(cell::type::interval)[0]; auto& trans2 = _eperm.at(cell::type::triangle); auto& trans3 = _eperm_inv.at(cell::type::triangle); if (!dofs[1][0].empty()) { precompute::apply_permutation(trans1, std::span(rot), dofs[1][0][0]); } if (!dofs[1][1].empty()) { precompute::apply_permutation(trans1, std::span(rot), dofs[1][1][0]); } if (!dofs[2][0].empty()) { precompute::apply_permutation(trans2[0], std::span(rot), dofs[2][0][0]); } if (!dofs[1][0].empty()) { precompute::apply_permutation(trans1, std::span(ref), dofs[1][0][0]); } if (!dofs[2][0].empty()) { precompute::apply_permutation(trans2[1], std::span(ref), dofs[2][0][0]); } if (!dofs[1][1].empty()) { precompute::apply_permutation(trans1, std::span(rot_inv), dofs[1][1][0]); } if (!dofs[1][2].empty()) { precompute::apply_permutation(trans1, std::span(rot_inv), dofs[1][2][0]); } if (!dofs[2][0].empty()) { precompute::apply_permutation(trans3[0], std::span(rot_inv), dofs[2][0][0]); } } precompute::prepare_permutation(rot); precompute::prepare_permutation(rot_inv); precompute::prepare_permutation(ref); auto& secp = _subentity_closure_perm.try_emplace(cell::type::triangle) .first->second; secp.push_back(rot); secp.push_back(ref); auto& secpi = _subentity_closure_perm_inv.try_emplace(cell::type::triangle) .first->second; secpi.push_back(rot_inv); secpi.push_back(ref); } if (_cell_type == cell::type::hexahedron || cell_type == cell::type::prism || cell_type == cell::type::pyramid) { // quadrilateral const int face_n = cell_type == cell::type::prism ? 1 : 0; int dof_n = 0; const auto conn = cell::sub_entity_connectivity(_cell_type)[2][face_n]; std::vector>> dofs; dofs.resize(3); for (int dim = 0; dim <= 2; ++dim) { dofs[dim].resize(conn[dim].size()); for (std::size_t i = 0; i < conn[dim].size(); ++i) { const int e = conn[dim][i]; for (int j : _edofs[dim][e]) { std::ignore = j; dofs[dim][i].push_back(dof_n++); } } } std::vector rot; // Vertices rot.insert(rot.end(), dofs[0][1].begin(), dofs[0][1].end()); rot.insert(rot.end(), dofs[0][3].begin(), dofs[0][3].end()); rot.insert(rot.end(), dofs[0][0].begin(), dofs[0][0].end()); rot.insert(rot.end(), dofs[0][2].begin(), dofs[0][2].end()); // Edges rot.insert(rot.end(), dofs[1][2].begin(), dofs[1][2].end()); rot.insert(rot.end(), dofs[1][0].begin(), dofs[1][0].end()); rot.insert(rot.end(), dofs[1][3].begin(), dofs[1][3].end()); rot.insert(rot.end(), dofs[1][1].begin(), dofs[1][1].end()); // Face rot.insert(rot.end(), dofs[2][0].begin(), dofs[2][0].end()); std::vector rot_inv; // Vertices rot_inv.insert(rot_inv.end(), dofs[0][2].begin(), dofs[0][2].end()); rot_inv.insert(rot_inv.end(), dofs[0][0].begin(), dofs[0][0].end()); rot_inv.insert(rot_inv.end(), dofs[0][3].begin(), dofs[0][3].end()); rot_inv.insert(rot_inv.end(), dofs[0][1].begin(), dofs[0][1].end()); // Edges rot_inv.insert(rot_inv.end(), dofs[1][1].begin(), dofs[1][1].end()); rot_inv.insert(rot_inv.end(), dofs[1][3].begin(), dofs[1][3].end()); rot_inv.insert(rot_inv.end(), dofs[1][0].begin(), dofs[1][0].end()); rot_inv.insert(rot_inv.end(), dofs[1][2].begin(), dofs[1][2].end()); // Face rot_inv.insert(rot_inv.end(), dofs[2][0].begin(), dofs[2][0].end()); std::vector ref; ref.insert(ref.end(), dofs[0][0].begin(), dofs[0][0].end()); ref.insert(ref.end(), dofs[0][2].begin(), dofs[0][2].end()); ref.insert(ref.end(), dofs[0][1].begin(), dofs[0][1].end()); ref.insert(ref.end(), dofs[0][3].begin(), dofs[0][3].end()); // Edges ref.insert(ref.end(), dofs[1][1].begin(), dofs[1][1].end()); ref.insert(ref.end(), dofs[1][0].begin(), dofs[1][0].end()); ref.insert(ref.end(), dofs[1][3].begin(), dofs[1][3].end()); ref.insert(ref.end(), dofs[1][2].begin(), dofs[1][2].end()); // Face ref.insert(ref.end(), dofs[2][0].begin(), dofs[2][0].end()); if (!_dof_transformations_are_identity) { auto& trans1 = _eperm.at(cell::type::interval)[0]; auto& trans2 = _eperm.at(cell::type::quadrilateral); auto& trans3 = _eperm_inv.at(cell::type::quadrilateral); if (!dofs[1][1].empty()) { precompute::apply_permutation(trans1, std::span(rot), dofs[1][1][0]); } if (!dofs[1][2].empty()) { precompute::apply_permutation(trans1, std::span(rot), dofs[1][2][0]); } if (!dofs[2][0].empty()) { precompute::apply_permutation(trans2[0], std::span(rot), dofs[2][0][0]); } if (!dofs[2][0].empty()) { precompute::apply_permutation(trans2[1], std::span(ref), dofs[2][0][0]); } if (!dofs[1][1].empty()) { precompute::apply_permutation(trans1, std::span(rot_inv), dofs[1][1][0]); } if (!dofs[1][2].empty()) { precompute::apply_permutation(trans1, std::span(rot_inv), dofs[1][2][0]); } if (!dofs[2][0].empty()) { precompute::apply_permutation(trans3[0], std::span(rot_inv), dofs[2][0][0]); } } precompute::prepare_permutation(rot); precompute::prepare_permutation(rot_inv); precompute::prepare_permutation(ref); auto& secp = _subentity_closure_perm.try_emplace(cell::type::quadrilateral) .first->second; secp.push_back(rot); secp.push_back(ref); auto& secpi = _subentity_closure_perm_inv.try_emplace(cell::type::quadrilateral) .first->second; secpi.push_back(rot_inv); secpi.push_back(ref); } } // Check if interpolation matrix is the identity mdspan_t matM(_matM.first.data(), _matM.second); _interpolation_is_identity = matM.extent(0) == matM.extent(1); for (std::size_t row = 0; _interpolation_is_identity && row < matM.extent(0); ++row) { for (std::size_t col = 0; col < matM.extent(1); ++col) { F v = col == row ? 1.0 : 0.0; constexpr F eps = 100 * std::numeric_limits::epsilon(); if (std::abs(matM(row, col) - v) > eps) { _interpolation_is_identity = false; break; } } } } /// @endcond //----------------------------------------------------------------------------- template bool FiniteElement::operator==(const FiniteElement& e) const { if (this == &e) return true; else if (family() == element::family::custom and e.family() == element::family::custom) { bool coeff_equal = false; if (_coeffs.first.size() == e.coefficient_matrix().first.size() and _coeffs.second == e.coefficient_matrix().second and std::ranges::equal(_coeffs.first, e.coefficient_matrix().first, [](auto x, auto y) { return std::abs(x - y) < 1.0e-10; })) { coeff_equal = true; } return cell_type() == e.cell_type() and discontinuous() == e.discontinuous() and map_type() == e.map_type() and sobolev_space() == e.sobolev_space() and value_shape() == e.value_shape() and embedded_superdegree() == e.embedded_superdegree() and embedded_subdegree() == e.embedded_subdegree() and coeff_equal and entity_dofs() == e.entity_dofs() and dof_ordering() == e.dof_ordering() and polyset_type() == e.polyset_type(); } else { return cell_type() == e.cell_type() and family() == e.family() and degree() == e.degree() and discontinuous() == e.discontinuous() and lagrange_variant() == e.lagrange_variant() and dpc_variant() == e.dpc_variant() and map_type() == e.map_type() and sobolev_space() == e.sobolev_space() and dof_ordering() == e.dof_ordering(); } } //----------------------------------------------------------------------------- template std::size_t FiniteElement::hash() const { std::size_t dof_ordering_hash = 0; for (std::size_t i = 0; i < dof_ordering().size(); ++i) { if (dof_ordering()[i] != static_cast(i)) { combine_hashes(dof_ordering_hash, std::hash{}(dof_ordering()[i] - i)); } } std::size_t h = std::hash{}(static_cast(family())); combine_hashes(h, dof_ordering_hash); combine_hashes(h, dof_ordering_hash); combine_hashes(h, std::hash{}(static_cast(cell_type()))); combine_hashes(h, std::hash{}(static_cast(lagrange_variant()))); combine_hashes(h, std::hash{}(static_cast(dpc_variant()))); combine_hashes(h, std::hash{}(static_cast(sobolev_space()))); combine_hashes(h, std::hash{}(static_cast(map_type()))); if (family() == element::family::custom) { std::size_t coeff_hash = 0; for (auto i : _coeffs.first) { // This takes five decimal places of each matrix entry. We should revisit // this combine_hashes(coeff_hash, int(i * 100000)); } std::size_t vs_hash = 0; for (std::size_t i = 0; i < value_shape().size(); ++i) { combine_hashes(vs_hash, std::hash{}(value_shape()[i])); } combine_hashes(h, coeff_hash); combine_hashes(h, std::hash{}(embedded_superdegree())); combine_hashes(h, std::hash{}(embedded_subdegree())); combine_hashes(h, std::hash{}(static_cast(polyset_type()))); combine_hashes(h, vs_hash); } else { combine_hashes(h, std::hash{}(degree())); } return h; } //----------------------------------------------------------------------------- template std::pair, std::array> FiniteElement::tabulate(int nd, impl::mdspan_t x) const { std::array shape = tabulate_shape(nd, x.extent(0)); std::vector data(shape[0] * shape[1] * shape[2] * shape[3]); tabulate(nd, x, mdspan_t(data.data(), shape)); return {std::move(data), shape}; } //----------------------------------------------------------------------------- template std::pair, std::array> FiniteElement::tabulate(int nd, std::span x, std::array shape) const { std::array phishape = tabulate_shape(nd, shape[0]); std::vector datab(phishape[0] * phishape[1] * phishape[2] * phishape[3]); tabulate(nd, mdspan_t(x.data(), shape[0], shape[1]), mdspan_t(datab.data(), phishape)); return {std::move(datab), phishape}; } //----------------------------------------------------------------------------- template void FiniteElement::tabulate(int nd, impl::mdspan_t x, mdspan_t basis_data) const { if (x.extent(1) != _cell_tdim) { throw std::runtime_error("Point dim (" + std::to_string(x.extent(1)) + ") does not match element dim (" + std::to_string(_cell_tdim) + ")."); } const std::size_t psize = polyset::dim(_cell_type, _poly_type, _embedded_superdegree); const std::array bsize = {(std::size_t)polyset::nderivs(_cell_type, nd), psize, x.extent(0)}; std::vector basis_b(bsize[0] * bsize[1] * bsize[2]); mdspan_t basis(basis_b.data(), bsize); polyset::tabulate(basis, _cell_type, _poly_type, _embedded_superdegree, nd, x); const int vs = std::accumulate(_value_shape.begin(), _value_shape.end(), 1, std::multiplies{}); std::vector C_b(_coeffs.second[0] * psize); mdspan_t C(C_b.data(), _coeffs.second[0], psize); mdspan_t coeffs_view(_coeffs.first.data(), _coeffs.second); std::vector result_b(C.extent(0) * bsize[2]); mdspan_t result(result_b.data(), C.extent(0), bsize[2]); for (std::size_t p = 0; p < basis.extent(0); ++p) { mdspan_t B(basis_b.data() + p * bsize[1] * bsize[2], bsize[1], bsize[2]); for (int j = 0; j < vs; ++j) { for (std::size_t k0 = 0; k0 < coeffs_view.extent(0); ++k0) for (std::size_t k1 = 0; k1 < psize; ++k1) C(k0, k1) = coeffs_view(k0, k1 + psize * j); math::dot(C, mdspan_t(B.data_handle(), B.extent(0), B.extent(1)), result); if (_dof_ordering.empty()) { for (std::size_t k0 = 0; k0 < basis_data.extent(1); ++k0) for (std::size_t k1 = 0; k1 < basis_data.extent(2); ++k1) basis_data(p, k0, k1, j) = result(k1, k0); } else { for (std::size_t k0 = 0; k0 < basis_data.extent(1); ++k0) for (std::size_t k1 = 0; k1 < basis_data.extent(2); ++k1) basis_data(p, k0, _dof_ordering[k1], j) = result(k1, k0); } } } } //----------------------------------------------------------------------------- template void FiniteElement::tabulate(int nd, std::span x, std::array xshape, std::span basis) const { std::array shape = tabulate_shape(nd, xshape[0]); assert(x.size() == xshape[0] * xshape[1]); assert(basis.size() == shape[0] * shape[1] * shape[2] * shape[3]); tabulate(nd, mdspan_t(x.data(), xshape), mdspan_t(basis.data(), shape)); } //----------------------------------------------------------------------------- template std::pair, std::array> FiniteElement::base_transformations() const { const std::size_t nt = num_transformations(this->cell_type()); const std::size_t ndofs = this->dim(); std::array shape = {nt, ndofs, ndofs}; std::vector bt_b(shape[0] * shape[1] * shape[2], 0); mdspan_t bt(bt_b.data(), shape); for (std::size_t i = 0; i < nt; ++i) for (std::size_t j = 0; j < ndofs; ++j) bt(i, j, j) = 1.0; std::size_t dofstart = 0; if (_cell_tdim > 0) { for (auto& edofs0 : _edofs[0]) dofstart += edofs0.size(); } int transform_n = 0; if (_cell_tdim > 1) { // Base transformations for edges { auto& tmp_data = _entity_transformations.at(cell::type::interval); mdspan_t tmp(tmp_data.first.data(), tmp_data.second); for (auto& e : _edofs[1]) { std::size_t ndofs = e.size(); for (std::size_t i = 0; i < ndofs; ++i) for (std::size_t j = 0; j < ndofs; ++j) bt(transform_n, i + dofstart, j + dofstart) = tmp(0, i, j); ++transform_n; dofstart += ndofs; } } if (_cell_tdim > 2) { for (std::size_t f = 0; f < _edofs[2].size(); ++f) { if (std::size_t ndofs = _edofs[2][f].size(); ndofs > 0) { auto& tmp_data = _entity_transformations.at(_cell_subentity_types[2][f]); mdspan_t tmp(tmp_data.first.data(), tmp_data.second); for (std::size_t i = 0; i < ndofs; ++i) for (std::size_t j = 0; j < ndofs; ++j) bt(transform_n, i + dofstart, j + dofstart) = tmp(0, i, j); ++transform_n; for (std::size_t i = 0; i < ndofs; ++i) for (std::size_t j = 0; j < ndofs; ++j) bt(transform_n, i + dofstart, j + dofstart) = tmp(1, i, j); ++transform_n; dofstart += ndofs; } } } } return {std::move(bt_b), shape}; } //----------------------------------------------------------------------------- template std::pair, std::array> FiniteElement::push_forward(impl::mdspan_t U, impl::mdspan_t J, std::span detJ, impl::mdspan_t K) const { const std::size_t physical_value_size = compute_value_size(_map_type, J.extent(1)); std::array shape = {U.extent(0), U.extent(1), physical_value_size}; std::vector ub(shape[0] * shape[1] * shape[2]); mdspan_t u(ub.data(), shape); using u_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< F, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>; using U_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const F, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>; using J_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const F, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>; using K_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const F, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>; auto map = this->map_fn(); for (std::size_t i = 0; i < u.extent(0); ++i) { u_t _u(u.data_handle() + i * u.extent(1) * u.extent(2), u.extent(1), u.extent(2)); U_t _U(U.data_handle() + i * U.extent(1) * U.extent(2), U.extent(1), U.extent(2)); J_t _J(J.data_handle() + i * J.extent(1) * J.extent(2), J.extent(1), J.extent(2)); K_t _K(K.data_handle() + i * K.extent(1) * K.extent(2), K.extent(1), K.extent(2)); map(_u, _U, _J, detJ[i], _K); } return {std::move(ub), shape}; } //----------------------------------------------------------------------------- template std::pair, std::array> FiniteElement::pull_back(impl::mdspan_t u, impl::mdspan_t J, std::span detJ, impl::mdspan_t K) const { const std::size_t reference_value_size = std::accumulate( _value_shape.begin(), _value_shape.end(), 1, std::multiplies{}); std::array shape = {u.extent(0), u.extent(1), reference_value_size}; std::vector Ub(shape[0] * shape[1] * shape[2]); mdspan_t U(Ub.data(), shape); using u_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const F, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>; using U_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< F, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>; using J_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const F, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>; using K_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const F, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>; auto map = this->map_fn(); for (std::size_t i = 0; i < u.extent(0); ++i) { u_t _u(u.data_handle() + i * u.extent(1) * u.extent(2), u.extent(1), u.extent(2)); U_t _U(U.data_handle() + i * U.extent(1) * U.extent(2), U.extent(1), U.extent(2)); J_t _J(J.data_handle() + i * J.extent(1) * J.extent(2), J.extent(1), J.extent(2)); K_t _K(K.data_handle() + i * K.extent(1) * K.extent(2), K.extent(1), K.extent(2)); map(_U, _u, _K, 1.0 / detJ[i], _J); } return {std::move(Ub), shape}; } //----------------------------------------------------------------------------- std::string basix::version() { static const std::string version_str = str(BASIX_VERSION); return version_str; } //----------------------------------------------------------------------------- /// @cond template class basix::FiniteElement; template class basix::FiniteElement; /// @endcond //----------------------------------------------------------------------------- fenics-basix-0.9.0/cpp/basix/finite-element.h000066400000000000000000002247631470517546000210700ustar00rootroot00000000000000// Copyright (c) 2020-2024 Chris Richardson, Matthew Scroggs and Garth . Wells // FEniCS Project // SPDX-License-Identifier: MIT #pragma once #include "cell.h" #include "element-families.h" #include "maps.h" #include "mdspan.hpp" #include "polyset.h" #include "precompute.h" #include "sobolev-spaces.h" #include #include #include #include #include #include #include #include #include #include #include /// Basix: FEniCS runtime basis evaluation library namespace basix { namespace impl { template using mdspan_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>; template using mdarray_t = MDSPAN_IMPL_STANDARD_NAMESPACE::MDSPAN_IMPL_PROPOSED_NAMESPACE::mdarray< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>; /// Create a container of cmdspan2_t objects from a container of /// mdarray2_t objects template std::array>, 4> to_mdspan(std::array>, 4>& x) { std::array>, 4> x1; for (std::size_t i = 0; i < x.size(); ++i) for (std::size_t j = 0; j < x[i].size(); ++j) x1[i].emplace_back(x[i][j].data(), x[i][j].extents()); return x1; } /// Create a container of cmdspan4_t objects from a container of /// mdarray4_t objects template std::array>, 4> to_mdspan(std::array>, 4>& M) { std::array>, 4> M1; for (std::size_t i = 0; i < M.size(); ++i) for (std::size_t j = 0; j < M[i].size(); ++j) M1[i].emplace_back(M[i][j].data(), M[i][j].extents()); return M1; } /// Create a container of cmdspan2_t objects from containers holding /// data buffers and shapes template std::array>, 4> to_mdspan(const std::array>, 4>& x, const std::array>, 4>& shape) { std::array>, 4> x1; for (std::size_t i = 0; i < x.size(); ++i) for (std::size_t j = 0; j < x[i].size(); ++j) x1[i].push_back(mdspan_t(x[i][j].data(), shape[i][j])); return x1; } /// Create a container of cmdspan4_t objects from containers holding /// data buffers and shapes template std::array>, 4> to_mdspan(const std::array>, 4>& M, const std::array>, 4>& shape) { std::array>, 4> M1; for (std::size_t i = 0; i < M.size(); ++i) for (std::size_t j = 0; j < M[i].size(); ++j) M1[i].push_back(mdspan_t(M[i][j].data(), shape[i][j])); return M1; } } // namespace impl namespace element { /// Typedef for mdspan template using mdspan_t = impl::mdspan_t; /// Create a version of the interpolation points, interpolation /// matrices and entity transformation that represent a discontinuous /// version of the element. This discontinuous version will have the /// same DOFs but they will all be associated with the interior of the /// reference cell. /// @param[in] x Interpolation points. Indices are (tdim, entity index, /// point index, dim) /// @param[in] M The interpolation matrices. Indices are (tdim, entity /// index, dof, vs, point_index, derivative) /// @param[in] tdim The topological dimension of the cell the element is /// defined on /// @param[in] value_size The value size of the element /// @return (xdata, xshape, Mdata, Mshape), where the x and M data are /// for a discontinuous version of the element (with the same shapes as /// x and M) template std::tuple>, 4>, std::array>, 4>, std::array>, 4>, std::array>, 4>> make_discontinuous(const std::array>, 4>& x, const std::array>, 4>& M, std::size_t tdim, std::size_t value_size); } // namespace element /// @brief A finite element. /// /// The basis of a finite element is stored as a set of coefficients, /// which are applied to the underlying expansion set for that cell /// type, when tabulating. template class FiniteElement { template using mdspan_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>; public: /// @brief Construct a finite element. /// /// Initialising a finite element calculates the basis functions of /// the finite element, in terms of the polynomial basis. /// /// The below explanation uses Einstein notation. /// /// The basis functions @f${\phi_i}@f$ of a finite element are represented /// as a linear combination of polynomials @f$\{p_j\}@f$ in an underlying /// polynomial basis that span the space of all d-dimensional polynomials up /// to order @f$k \ (P_k^d)@f$: /// \f[ \phi_i = c_{ij} p_j \f] /// /// In some cases, the basis functions @f$\{\phi_i\}@f$ do not span the /// full space @f$P_k@f$, in which case we denote space spanned by the /// basis functions by @f$\{q_k\}@f$, which can be represented by: /// @f[ q_i = b_{ij} p_j. @f] /// This leads to /// @f[ \phi_i = c^{\prime}_{ij} q_j = c^{\prime}_{ij} b_{jk} p_k, @f] /// and in matrix form: /// \f[ /// \phi = C^{\prime} B p /// \f] /// /// If the basis functions span the full space, then @f$ B @f$ is simply /// the identity. /// /// The basis functions @f$\phi_i@f$ are defined by a dual set of functionals /// @f$\{f_i\}@f$. The basis functions are the functions in span{@f$q_k@f$} /// such that /// @f[ f_i(\phi_j) = \delta_{ij} @f] /// and inserting the expression for @f$\phi_{j}@f$: /// @f[ f_i(c^{\prime}_{jk}b_{kl}p_{l}) = c^{\prime}_{jk} b_{kl} f_i \left( /// p_{l} \right) @f] /// /// Defining a matrix D given by applying the functionals to each /// polynomial @f$p_j@f$: /// @f[ [D] = d_{ij},\mbox{ where } d_{ij} = f_i(p_j), @f] /// we have: /// @f[ C^{\prime} B D^{T} = I @f] /// /// and /// /// @f[ C^{\prime} = (B D^{T})^{-1}. @f] /// /// Recalling that @f$C = C^{\prime} B@f$, where @f$C@f$ is the matrix /// form of @f$c_{ij}@f$, /// /// @f[ C = (B D^{T})^{-1} B @f] /// /// This function takes the matrices @f$B@f$ (`wcoeffs`) and @f$D@f$ (`M`) as /// inputs and will internally compute @f$C@f$. /// /// The matrix @f$BD^{T}@f$ can be obtained from an element by using /// dual_matrix(). The matrix @f$C@f$ can be obtained from an element /// by using coefficient_matrix(). /// /// Example: Order 1 Lagrange elements on a triangle /// ------------------------------------------------ /// On a triangle, the scalar expansion basis is: /// @f[ p_0 = \sqrt{2}/2 \qquad /// p_1 = \sqrt{3}(2x + y - 1) \qquad /// p_2 = 3y - 1 @f] /// These span the space @f$P_1@f$. /// /// Lagrange order 1 elements span the space P_1, so in this example, /// B (span_coeffs) is the identity matrix: /// @f[ B = \begin{bmatrix} /// 1 & 0 & 0 \\ /// 0 & 1 & 0 \\ /// 0 & 0 & 1 \end{bmatrix} @f] /// /// The functionals defining the Lagrange order 1 space are point /// evaluations at the three vertices of the triangle. The matrix D /// (dual) given by applying these to p_0 to p_2 is: /// @f[ \mbox{dual} = \begin{bmatrix} /// \sqrt{2}/2 & -\sqrt{3} & -1 \\ /// \sqrt{2}/2 & \sqrt{3} & -1 \\ /// \sqrt{2}/2 & 0 & 2 \end{bmatrix} @f] /// /// For this example, this function outputs the matrix: /// @f[ C = \begin{bmatrix} /// \sqrt{2}/3 & -\sqrt{3}/6 & -1/6 \\ /// \sqrt{2}/3 & \sqrt{3}/6 & -1/6 \\ /// \sqrt{2}/3 & 0 & 1/3 \end{bmatrix} @f] /// The basis functions of the finite element can be obtained by applying /// the matrix C to the vector @f$[p_0, p_1, p_2]@f$, giving: /// @f[ \begin{bmatrix} 1 - x - y \\ x \\ y \end{bmatrix} @f] /// /// Example: Order 1 Raviart-Thomas on a triangle /// --------------------------------------------- /// On a triangle, the 2D vector expansion basis is: /// @f[ \begin{matrix} /// p_0 & = & (\sqrt{2}/2, 0) \\ /// p_1 & = & (\sqrt{3}(2x + y - 1), 0) \\ /// p_2 & = & (3y - 1, 0) \\ /// p_3 & = & (0, \sqrt{2}/2) \\ /// p_4 & = & (0, \sqrt{3}(2x + y - 1)) \\ /// p_5 & = & (0, 3y - 1) /// \end{matrix} /// @f] /// These span the space @f$ P_1^2 @f$. /// /// Raviart-Thomas order 1 elements span a space smaller than @f$ P_1^2 @f$, /// so B (span_coeffs) is not the identity. It is given by: /// @f[ B = \begin{bmatrix} /// 1 & 0 & 0 & 0 & 0 & 0 \\ /// 0 & 0 & 0 & 1 & 0 & 0 \\ /// 1/12 & \sqrt{6}/48 & -\sqrt{2}/48 & 1/12 & 0 & \sqrt{2}/24 /// \end{bmatrix} /// @f] /// Applying the matrix B to the vector @f$[p_0, p_1, ..., p_5]@f$ gives the /// basis of the polynomial space for Raviart-Thomas: /// @f[ \begin{bmatrix} /// \sqrt{2}/2 & 0 \\ /// 0 & \sqrt{2}/2 \\ /// \sqrt{2}x/8 & \sqrt{2}y/8 /// \end{bmatrix} @f] /// /// The functionals defining the Raviart-Thomas order 1 space are integral /// of the normal components along each edge. The matrix D (dual) given /// by applying these to @f$p_0@f$ to @f$p_5@f$ is: /// @f[ D = \begin{bmatrix} /// -\sqrt{2}/2 & -\sqrt{3}/2 & -1/2 & -\sqrt{2}/2 & -\sqrt{3}/2 & -1/2 \\ /// -\sqrt{2}/2 & \sqrt{3}/2 & -1/2 & 0 & 0 & 0 \\ /// 0 & 0 & 0 & \sqrt{2}/2 & 0 & -1 /// \end{bmatrix} @f] /// /// In this example, this function outputs the matrix: /// @f[ C = \begin{bmatrix} /// -\sqrt{2}/2 & -\sqrt{3}/2 & -1/2 & -\sqrt{2}/2 & -\sqrt{3}/2 & -1/2 \\ /// -\sqrt{2}/2 & \sqrt{3}/2 & -1/2 & 0 & 0 & 0 \\ /// 0 & 0 & 0 & \sqrt{2}/2 & 0 & -1 /// \end{bmatrix} @f] /// The basis functions of the finite element can be obtained by applying /// the matrix C to the vector @f$[p_0, p_1, ..., p_5]@f$, giving: /// @f[ \begin{bmatrix} /// -x & -y \\ /// x - 1 & y \\ /// -x & 1 - y \end{bmatrix} @f] /// /// @param[in] family The element family /// @param[in] cell_type The cell type /// @param[in] poly_type The polyset type /// @param[in] degree The degree of the element /// @param[in] interpolation_nderivs The number of derivatives that /// need to be used during interpolation /// @param[in] value_shape The value shape of the element /// @param[in] wcoeffs Matrices for the kth value index containing the /// expansion coefficients defining a polynomial basis spanning the /// polynomial space for this element. Shape is (dim(finite element /// polyset), dim(Legendre polynomials)) /// @param[in] x Interpolation points. Indices are (tdim, entity /// index, point index, dim) /// @param[in] M The interpolation matrices. Indices are (tdim, entity /// index, dof, vs, point_index, derivative) /// @param[in] map_type The type of map to be used to map values from /// the reference to a cell /// @param[in] sobolev_space The underlying Sobolev space for the /// element /// @param[in] discontinuous Indicates whether or not this is the /// discontinuous version of the element /// @param[in] embedded_subdegree The highest degree n such that /// a Lagrange (or vector Lagrange) element of degree n is a subspace /// of this element /// @param[in] embedded_superdegree The highest degree n such that at least /// one polynomial of degree n is included in this element's /// polymonial set /// @param[in] lvariant The Lagrange variant of the element /// @param[in] dvariant The DPC variant of the element /// @param[in] dof_ordering DOF reordering: a mapping from the /// reference order to a new permuted order FiniteElement(element::family family, cell::type cell_type, polyset::type poly_type, int degree, const std::vector& value_shape, mdspan_t wcoeffs, const std::array>, 4>& x, const std::array>, 4>& M, int interpolation_nderivs, maps::type map_type, sobolev::space sobolev_space, bool discontinuous, int embedded_subdegree, int embedded_superdegree, element::lagrange_variant lvariant, element::dpc_variant dvariant, std::vector dof_ordering = {}); /// Copy constructor FiniteElement(const FiniteElement& element) = default; /// Move constructor FiniteElement(FiniteElement&& element) = default; /// Destructor ~FiniteElement() = default; /// Assignment operator FiniteElement& operator=(const FiniteElement& element) = default; /// Move assignment operator FiniteElement& operator=(FiniteElement&& element) = default; /// @brief Check if two elements are the same /// @note This operator compares the element properties, e.g. family, /// degree, etc, and not computed numerical data /// @return True if elements are the same bool operator==(const FiniteElement& e) const; /// Get a unique hash of this element std::size_t hash() const; /// @brief Array shape for tabulate basis values and derivatives at /// set of points. /// /// @param[in] nd The order of derivatives, up to and including, to /// compute. Use 0 for the basis functions only. /// @param[in] num_points Number of points that basis will be computed /// at. /// @return The shape of the array to will filled when passed to /// tabulate(). std::array tabulate_shape(std::size_t nd, std::size_t num_points) const { std::size_t ndsize = 1; for (std::size_t i = 1; i <= nd; ++i) ndsize *= (_cell_tdim + i); for (std::size_t i = 1; i <= nd; ++i) ndsize /= i; std::size_t vs = std::accumulate(_value_shape.begin(), _value_shape.end(), 1, std::multiplies{}); std::size_t ndofs = _coeffs.second[0]; return {ndsize, num_points, ndofs, vs}; } /// @brief Compute basis values and derivatives at set of points. /// /// @note The version of tabulate() with the basis data as an out /// argument should be preferred for repeated call where performance /// is critical. /// /// @param[in] nd The order of derivatives, up to and including, to /// compute. Use 0 for the basis functions only. /// @param[in] x The points at which to compute the basis functions. /// The shape of x is (number of points, geometric dimension). /// @return The basis functions (and derivatives). The shape is /// (derivative, point, basis fn index, value index). /// - The first index is the derivative, with higher derivatives are /// stored in triangular (2D) or tetrahedral (3D) ordering, ie for /// the (x,y) derivatives in 2D: (0,0), (1,0), (0,1), (2,0), (1,1), /// (0,2), (3,0)... The function basix::indexing::idx can be used to find the /// appropriate derivative. /// - The second index is the point index /// - The third index is the basis function index /// - The fourth index is the basis function component. Its has size /// one for scalar basis functions. std::pair, std::array> tabulate(int nd, impl::mdspan_t x) const; /// @brief Compute basis values and derivatives at set of points. /// /// @note The version of tabulate() with the basis data as an out /// argument should be preferred for repeated call where performance /// is critical /// /// @param[in] nd The order of derivatives, up to and including, to /// compute. Use 0 for the basis functions only. /// @param[in] x The points at which to compute the basis functions /// (row-major storage). /// @param[in] shape The shape `(number of points, geometric /// dimension)` of the `x` array. /// @return The basis functions (and derivatives). The shape is /// (derivative, point, basis fn index, value index). /// - The first index is the derivative, with higher derivatives are /// stored in triangular (2D) or tetrahedral (3D) ordering, ie for /// the (x,y) derivatives in 2D: (0,0), (1,0), (0,1), (2,0), (1,1), /// (0,2), (3,0)... The function indexing::idx can be used to find the /// appropriate derivative. /// - The second index is the point index /// - The third index is the basis function index /// - The fourth index is the basis function component. Its has size /// one for scalar basis functions. std::pair, std::array> tabulate(int nd, std::span x, std::array shape) const; /// @brief Compute basis values and derivatives at set of points. /// /// @note This function is designed to be called at runtime, so its /// performance is critical. /// /// @param[in] nd The order of derivatives, up to and including, to /// compute. Use 0 for the basis functions only. /// @param[in] x The points at which to compute the basis functions. /// The shape of x is (number of points, geometric dimension). /// @param [out] basis Memory location to fill. It must be allocated /// with shape `(num_derivatives, num_points, num basis functions, /// value_size)`. The function tabulate_shape() can be used to get the /// required shape. /// - The first index is the derivative, with higher derivatives are /// stored in triangular (2D) or tetrahedral (3D) ordering, ie for /// the (x,y) derivatives in 2D: (0,0), (1,0), (0,1), (2,0), (1,1), /// (0,2), (3,0)... The function indexing::idx can be used to /// find the appropriate derivative. /// - The second index is the point index /// - The third index is the basis function index /// - The fourth index is the basis function component. Its has size /// one for scalar basis functions. /// /// @todo Remove all internal dynamic memory allocation, pass scratch /// space as required void tabulate(int nd, impl::mdspan_t x, mdspan_t basis) const; /// @brief Compute basis values and derivatives at set of points. /// /// @note This function is designed to be called at runtime, so its /// performance is critical. /// /// @param[in] nd The order of derivatives, up to and including, to /// compute. Use 0 for the basis functions only. /// @param[in] x The points at which to compute the basis functions /// (row-major storage). The shape of `x` is `(number of points, /// geometric dimension)`. /// @param[in] xshape The shape `(number of points, geometric /// dimension)` of `x`. /// @param [out] basis Memory location to fill. It must be allocated /// with shape `(num_derivatives, num_points, num basis functions, /// value_size)`. The function tabulate_shape() can be used to get the /// required shape. /// - The first index is the derivative, with higher derivatives are /// stored in triangular (2D) or tetrahedral (3D) ordering, ie for the /// (x,y) derivatives in 2D: (0,0), (1,0), (0,1), (2,0), (1,1), (0,2), /// (3,0)... The function indexing::idx can be used to find the /// appropriate derivative. /// - The second index is the point index /// - The third index is the basis function index /// - The fourth index is the basis function component. Its has size /// one for scalar basis functions. void tabulate(int nd, std::span x, std::array xshape, std::span basis) const; /// @brief Get the element cell type. /// @return The cell type cell::type cell_type() const { return _cell_type; } /// @brief Get the element polyset type. /// @return The polyset polyset::type polyset_type() const { return _poly_type; } /// @brief Get the element polynomial degree. /// @return Polynomial degree int degree() const { return _degree; } /// @brief Lowest degree `n` such that the highest degree polynomial in this /// element is contained in a Lagrange (or vector Lagrange) element of /// degree `n`. /// @return Polynomial degree int embedded_superdegree() const { return _embedded_superdegree; } /// @brief Highest degree `n` such that a Lagrange (or vector Lagrange) /// element of degree n is a subspace of this element. /// @return Polynomial degree int embedded_subdegree() const { return _embedded_subdegree; } /// @brief Element value tensor shape. /// /// For example, returns `{}` for scalars, `{3}` for vectors in 3D, /// `{2, 2}` for a rank-2 tensor in 2D. /// @return Value shape const std::vector& value_shape() const { return _value_shape; } /// @brief Dimension of the finite element space. /// /// The dimension is the number of degrees-of-freedom for the element. /// @return Number of degrees of freedom int dim() const { return _coeffs.second[0]; } /// @brief The finite element family. /// @return The family element::family family() const { return _family; } /// @brief Lagrange variant of the element. /// @return The Lagrange variant. element::lagrange_variant lagrange_variant() const { return _lagrange_variant; } /// @brief DPC variant of the element. /// @return The DPC variant. element::dpc_variant dpc_variant() const { return _dpc_variant; } /// @brief Map type for the element. /// @return The map type. maps::type map_type() const { return _map_type; } /// @brief Underlying Sobolev space for this element. /// @return The Sobolev space. sobolev::space sobolev_space() const { return _sobolev_space; } /// @brief Indicates whether this element is the discontinuous /// variant. /// @return True if this element is a discontinuous version of the /// element. bool discontinuous() const { return _discontinuous; } /// @brief Indicates if the degree-of-freedom transformations are all /// permutations. bool dof_transformations_are_permutations() const { return _dof_transformations_are_permutations; } /// @brief Indicates is the dof transformations are all the identity. bool dof_transformations_are_identity() const { return _dof_transformations_are_identity; } /// @brief Map function values from the reference to a physical cell. /// /// This function can perform the mapping for multiple points, grouped /// by points that share a common Jacobian. /// @param[in] U The function values on the reference. The indices are /// `[Jacobian index, point index, components]`. /// @param[in] J The Jacobian of the mapping. The indices are /// `[Jacobian index, J_i, J_j]`. /// @param[in] detJ The determinant of the Jacobian of the mapping. It /// has length `J.shape(0)` /// @param[in] K The inverse of the Jacobian of the mapping. The /// indices are `[Jacobian index, K_i, K_j]`. /// @return The function values on the cell. The indices are [Jacobian /// index, point index, components]. std::pair, std::array> push_forward(impl::mdspan_t U, impl::mdspan_t J, std::span detJ, impl::mdspan_t K) const; /// @brief Map function values from a physical cell to the reference. /// @param[in] u The function values on the cell /// @param[in] J The Jacobian of the mapping /// @param[in] detJ The determinant of the Jacobian of the mapping /// @param[in] K The inverse of the Jacobian of the mapping /// @return The function values on the reference. The indices are /// [Jacobian index, point index, components]. std::pair, std::array> pull_back(impl::mdspan_t u, impl::mdspan_t J, std::span detJ, impl::mdspan_t K) const; /// @brief Return a function that performs the appropriate /// push-forward/pull-back for the element type. /// /// @tparam O The type that hold the (computed) mapped data (ndim==2) /// @tparam P The type that hold the data to be mapped (ndim==2) /// @tparam Q The type that holds the Jacobian (or inverse) matrix (ndim==2) /// @tparam R The type that holds the inverse of the `Q` data /// (ndim==2) /// /// @return A function that for a push-forward takes arguments /// - `u` [out] The data on the physical cell after the /// push-forward flattened with row-major layout, shape=(num_points, /// value_size) /// - `U` [in] The data on the reference cell physical field to push /// forward, flattened with row-major layout, shape=(num_points, /// ref_value_size) /// - `J` [in] The Jacobian matrix of the map ,shape=(gdim, tdim) /// - `detJ` [in] det(J) /// - `K` [in] The inverse of the Jacobian matrix, shape=(tdim, gdim) /// /// For a pull-back the arguments should be: /// - `U` [out] The data on the reference cell after the pull-back, /// flattened with row-major layout, shape=(num_points, ref /// value_size) /// - `u` [in] The data on the physical cell that should be pulled /// back , flattened with row-major layout, shape=(num_points, /// value_size) /// - `K` [in] The inverse of the Jacobian matrix of the map /// ,shape=(tdim, gdim) /// - `detJ_inv` [in] 1/det(J) /// - `J` [in] The Jacobian matrix, shape=(gdim, tdim) template std::function map_fn() const { switch (_map_type) { case maps::type::identity: return [](O& u, const P& U, const Q&, F, const R&) { assert(U.extent(0) == u.extent(0)); assert(U.extent(1) == u.extent(1)); for (std::size_t i = 0; i < U.extent(0); ++i) for (std::size_t j = 0; j < U.extent(1); ++j) u(i, j) = U(i, j); }; case maps::type::covariantPiola: return [](O& u, const P& U, const Q& J, F detJ, const R& K) { maps::covariant_piola(u, U, J, detJ, K); }; case maps::type::contravariantPiola: return [](O& u, const P& U, const Q& J, F detJ, const R& K) { maps::contravariant_piola(u, U, J, detJ, K); }; case maps::type::doubleCovariantPiola: return [](O& u, const P& U, const Q& J, F detJ, const R& K) { maps::double_covariant_piola(u, U, J, detJ, K); }; case maps::type::doubleContravariantPiola: return [](O& u, const P& U, const Q& J, F detJ, const R& K) { maps::double_contravariant_piola(u, U, J, detJ, K); }; default: throw std::runtime_error("Map not implemented"); } } /// @brief Get the dofs on each topological entity: (vertices, edges, /// faces, cell) in that order. /// /// For example, Lagrange degree 2 on a triangle has vertices: [[0], /// [1], [2]], edges: [[3], [4], [5]], cell: [[]] /// @return Dofs associated with an entity of a given topological /// dimension. The shape is (tdim + 1, num_entities, num_dofs). const std::vector>>& entity_dofs() const { return _edofs; } /// @brief Get the dofs on the closure of each topological entity: /// (vertices, edges, faces, cell) in that order. /// /// For example, Lagrange degree 2 on a triangle has vertices: [[0], /// [1], [2]], edges: [[1, 2, 3], [0, 2, 4], [0, 1, 5]], cell: [[0, 1, /// 2, 3, 4, 5]] /// @return Dofs associated with the closure of an entity of a given /// topological dimension. The shape is `(tdim + 1, num_entities, /// num_dofs)`. const std::vector>>& entity_closure_dofs() const { return _e_closure_dofs; } /// @brief Get the base transformations. /// /// The base transformations represent the effect of rotating or reflecting /// a subentity of the cell on the numbering and orientation of the DOFs. /// This returns a list of matrices with one matrix for each subentity /// permutation in the following order: /// Reversing edge 0, reversing edge 1, ... /// Rotate face 0, reflect face 0, rotate face 1, reflect face 1, ... /// /// Example: Order 3 Lagrange on a triangle /// --------------------------------------- /// This space has 10 dofs arranged like: /// ~~~~~~~~~~~~~~~~ /// 2 /// |\ /// 6 4 /// | \ /// 5 9 3 /// | \ /// 0-7-8-1 /// ~~~~~~~~~~~~~~~~ /// For this element, the base transformations are: /// [Matrix swapping 3 and 4, /// Matrix swapping 5 and 6, /// Matrix swapping 7 and 8] /// The first row shows the effect of reversing the diagonal edge. The /// second row shows the effect of reversing the vertical edge. The third /// row shows the effect of reversing the horizontal edge. /// /// Example: Order 1 Raviart-Thomas on a triangle /// --------------------------------------------- /// This space has 3 dofs arranged like: /// ~~~~~~~~~~~~~~~~ /// |\ /// | \ /// | \ /// <-1 0 /// | / \ /// | L ^ \ /// | | \ /// ---2--- /// ~~~~~~~~~~~~~~~~ /// These DOFs are integrals of normal components over the edges: DOFs 0 and 2 /// are oriented inward, DOF 1 is oriented outwards. /// For this element, the base transformation matrices are: /// ~~~~~~~~~~~~~~~~ /// 0: [[-1, 0, 0], /// [ 0, 1, 0], /// [ 0, 0, 1]] /// 1: [[1, 0, 0], /// [0, -1, 0], /// [0, 0, 1]] /// 2: [[1, 0, 0], /// [0, 1, 0], /// [0, 0, -1]] /// ~~~~~~~~~~~~~~~~ /// The first matrix reverses DOF 0 (as this is on the first edge). The second /// matrix reverses DOF 1 (as this is on the second edge). The third matrix /// reverses DOF 2 (as this is on the third edge). /// /// Example: DOFs on the face of Order 2 Nedelec first kind on a tetrahedron /// ------------------------------------------------------------------------ /// On a face of this tetrahedron, this space has two face tangent DOFs: /// ~~~~~~~~~~~~~~~~ /// |\ |\ /// | \ | \ /// | \ | ^\ /// | \ | | \ /// | 0->\ | 1 \ /// | \ | \ /// ------ ------ /// ~~~~~~~~~~~~~~~~ /// For these DOFs, the subblocks of the base transformation matrices are: /// ~~~~~~~~~~~~~~~~ /// rotation: [[-1, 1], /// [ 1, 0]] /// reflection: [[0, 1], /// [1, 0]] /// ~~~~~~~~~~~~~~~~ /// @return The base transformations for this element. The shape is /// (ntranformations, ndofs, ndofs) std::pair, std::array> base_transformations() const; /// @brief Return the entity dof transformation matrices /// @return The entity transformations for the sub-entities of this /// element. The shape for each cell is (ntransformations, ndofs, /// ndofs) std::map, std::array>> entity_transformations() const { return _entity_transformations; } /// @brief Permute indices associated with degree-of-freedoms on the /// reference element ordering to the globally consistent physical /// element degree-of-freedom ordering. /// /// Given an array \f$\tilde{d}\f$ that holds an integer associated /// with each degree-of-freedom and following the reference element /// degree-of-freedom ordering, this function computes /// \f[ /// d = P \tilde{d}, /// \f] /// where \f$P\f$ is a permutation matrix and \f$d\f$ holds the /// integers in \f$\tilde{d}\f$ but permuted to follow the globally /// consistent physical element degree-of-freedom ordering. The /// permutation is computed in-place. /// /// @note This function is designed to be called at runtime, so its /// performance is critical. /// /// @param[in,out] d Indices associated with each reference element /// degree-of-freedom (in). Indices associated with each physical /// element degree-of-freedom (out). /// @param cell_info Permutation info for the cell void permute(std::span d, std::uint32_t cell_info) const { if (!_dof_transformations_are_permutations) { throw std::runtime_error( "The DOF transformations for this element are not permutations"); } if (_dof_transformations_are_identity) return; else permute_data(d, 1, cell_info, _eperm); } /// @brief Perform the inverse of the operation applied by permute(). /// /// Given an array \f$d\f$ that holds an integer associated with each /// degree-of-freedom and following the globally consistent physical /// element degree-of-freedom ordering, this function computes /// \f[ /// \tilde{d} = P^{T} d, /// \f] /// where \f$P^{T}\f$ is a permutation matrix and \f$\tilde{d}\f$ /// holds the integers in \f$d\f$ but permuted to follow the reference /// element degree-of-freedom ordering. The permutation is computed /// in-place. /// /// @param[in,out] d Indices associated with each physical element /// degree-of-freedom [in]. Indices associated with each reference /// element degree-of-freedom [out]. /// @param cell_info Permutation info for the cell void permute_inv(std::span d, std::uint32_t cell_info) const { if (!_dof_transformations_are_permutations) { throw std::runtime_error( "The DOF transformations for this element are not permutations"); } if (_dof_transformations_are_identity) return; else permute_data(d, 1, cell_info, _eperm_inv); } /// @brief Permute indices associated with degree-of-freedoms on the /// closure of a sub-entity of the reference element /// /// This function performs a similar permutation to permute() but /// additionally permutes the positions of vertices and edges /// /// @note This function is designed to be called at runtime, so its /// performance is critical. /// /// @param[in,out] d Indices associated with each reference element /// degree-of-freedom (in). Indices associated with each physical /// element degree-of-freedom (out). /// @param cell_info Permutation info for the cell /// @param entity_type The cell type of the sub-entity /// @param entity_index The index of the entity void permute_subentity_closure(std::span d, std::uint32_t cell_info, cell::type entity_type, int entity_index) const { const int entity_dim = cell::topological_dimension(entity_type); int face_start = _cell_tdim == 3 ? 3 * _edofs[2].size() : 0; std::uint32_t entity_info = 0; switch (entity_dim) { case 0: return; case 1: entity_info = cell_info >> (face_start + entity_index) & 1; break; case 2: entity_info = cell_info >> (3 * entity_index) & 7; break; default: throw std::runtime_error("Unsupported cell dimension"); } permute_subentity_closure(d, entity_info, entity_type); } /// @brief Perform the inverse of the operation applied by /// permute_subentity_closure(). /// /// @note This function is designed to be called at runtime, so its /// performance is critical. /// /// @param[in,out] d Indices associated with each reference element /// degree-of-freedom (in). Indices associated with each physical /// element degree-of-freedom (out). /// @param cell_info Permutation info for the cell /// @param entity_type The cell type of the sub-entity /// @param entity_index The index of the entity void permute_subentity_closure_inv(std::span d, std::uint32_t cell_info, cell::type entity_type, int entity_index) const { const int entity_dim = cell::topological_dimension(entity_type); int face_start = _cell_tdim == 3 ? 3 * _edofs[2].size() : 0; std::uint32_t entity_info; switch (entity_dim) { case 0: return; case 1: entity_info = cell_info >> (face_start + entity_index) & 1; break; case 2: entity_info = cell_info >> (3 * entity_index) & 7; break; default: throw std::runtime_error("Unsupported cell dimension"); } permute_subentity_closure_inv(d, entity_info, entity_type); } /// @brief Permute indices associated with degree-of-freedoms on the /// closure of a sub-entity of the reference element /// /// This function performs a similar permutation to permute() but /// additionally permutes the positions of vertices and edges /// /// @note This function is designed to be called at runtime, so its /// performance is critical. /// /// @param[in,out] d Indices associated with each reference element /// degree-of-freedom (in). Indices associated with each physical /// element degree-of-freedom (out). /// @param entity_info Permutation info for the entity /// @param entity_type The cell type of the sub-entity void permute_subentity_closure(std::span d, std::uint32_t entity_info, cell::type entity_type) const { if (!_dof_transformations_are_permutations) { throw std::runtime_error( "The DOF transformations for this element are not permutations"); } const int entity_dim = cell::topological_dimension(entity_type); if (entity_dim == 0) return; auto& perm = _subentity_closure_perm.at(entity_type); if (entity_dim == 1) { if (entity_info & 1) { precompute::apply_permutation(perm[0], d); } } else if (entity_dim == 2) { // Rotate a face for (std::uint32_t r = 0; r < (entity_info >> 1 & 3); ++r) { precompute::apply_permutation(perm[0], d); } // Reflect a face (post rotate) if (entity_info & 1) { precompute::apply_permutation(perm[1], d); } } else { throw std::runtime_error( "Invalid dimension for permute_subentity_closure"); } } /// @brief Perform the inverse of the operation applied by /// permute_subentity_closure(). /// /// @note This function is designed to be called at runtime, so its /// performance is critical. /// /// @param[in,out] d Indices associated with each reference element /// degree-of-freedom (in). Indices associated with each physical /// element degree-of-freedom (out). /// @param entity_info Permutation info for the entity /// @param entity_type The cell type of the sub-entity void permute_subentity_closure_inv(std::span d, std::uint32_t entity_info, cell::type entity_type) const { if (!_dof_transformations_are_permutations) { throw std::runtime_error( "The DOF transformations for this element are not permutations"); } const int entity_dim = cell::topological_dimension(entity_type); if (entity_dim == 0) return; auto& perm = _subentity_closure_perm_inv.at(entity_type); if (entity_dim == 1) { if (entity_info & 1) { precompute::apply_permutation(perm[0], d); } } else if (entity_dim == 2) { // Reflect a face (pre rotate) if (entity_info & 1) { precompute::apply_permutation(perm[1], d); } // Rotate a face for (std::uint32_t r = 0; r < (entity_info >> 1 & 3); ++r) { precompute::apply_permutation(perm[0], d); } } else { throw std::runtime_error( "Invalid dimension for permute_subentity_closure"); } } /// @brief Transform basis functions from the reference element /// ordering and orientation to the globally consistent physical /// element ordering and orientation. /// /// Consider that the value of a finite element function \f$f_{h}\f$ /// at a point is given by /// \f[ /// f_{h} = \phi^{T} c, /// \f] /// where \f$f_{h}\f$ has shape \f$r \times 1\f$, \f$\phi\f$ has shape /// \f$d \times r\f$ and holds the finite element basis functions, /// and \f$c\f$ has shape \f$d \times 1\f$ and holds the /// degrees-of-freedom. The basis functions and /// degree-of-freedom are with respect to the physical element /// orientation. If the degrees-of-freedom on the physical element /// orientation are given by /// \f[ \phi = T \tilde{\phi}, \f] /// where \f$T\f$ is a \f$d \times d\f$ matrix, it follows from /// \f$f_{h} = \phi^{T} c = \tilde{\phi}^{T} T^{T} c\f$ that /// \f[ \tilde{c} = T^{T} c. \f] /// /// This function applies \f$T\f$ to data. The transformation is /// performed in-place. The operator \f$T\f$ is orthogonal for many /// elements, but not all. /// /// @param[in,out] u Data to transform. The shape is `(m, n)`, where /// `m` is the number of dgerees-of-freedom and the storage is /// row-major. /// @param[in] n Number of columns in `data`. /// @param cell_info Permutation info for the cell template void T_apply(std::span u, int n, std::uint32_t cell_info) const; /// @brief Apply the transpose of the operator applied by T_apply(). /// /// The transformation \f[ u \leftarrow T^{T} u \f] is performed /// in-place. /// /// @param[in,out] u Data to transform. The shape is `(m, n)`, where /// `m` is the number of dgerees-of-freedom an d the storage is /// row-major. /// @param[in] n Number of columns in `data`. /// @param[in] cell_info Permutation info for the cell, template void Tt_apply(std::span u, int n, std::uint32_t cell_info) const; /// @brief Apply the inverse transpose of the operator applied by /// T_apply(). /// /// The transformation \f[ u \leftarrow T^{-T} u \f] is performed /// in-place. /// /// @param[in,out] u Data to transform. The shape is `(m, n)`, where /// `m` is the number of dgerees-of-freedom and the storage is /// row-major. /// @param[in] n Number of columns in `data`. /// @param[in] cell_info Permutation info for the cell. template void Tt_inv_apply(std::span u, int n, std::uint32_t cell_info) const; /// @brief Apply the inverse of the operator applied by T_apply(). /// /// The transformation \f[ u \leftarrow T^{-1} u \f] is performed /// in-place. /// /// @param[in,out] u Data to transform. The shape is `(m, n)`, where /// `m` is the number of dgerees-of-freedom and the storage is /// row-major. /// @param[in] n Number of columns in `data`. /// @param[in] cell_info Permutation info for the cell. template void Tinv_apply(std::span u, int n, std::uint32_t cell_info) const; /// @brief Right(post)-apply the transpose of the operator applied by /// T_apply(). /// /// Computes \f[ u^{T} \leftarrow u^{T} T^{T} \f] in-place. /// /// @param[in,out] u Data to transform. The shape is `(m, n)`, where /// `m` is the number of dgerees-of-freedom and the storage is /// row-major. /// @param[in] n Number of columns in `data`. /// @param[in] cell_info Permutation info for the cell. template void Tt_apply_right(std::span u, int n, std::uint32_t cell_info) const; /// @brief Right(post)-apply the operator applied by T_apply(). /// /// Computes \f[ u^{T} \leftarrow u^{T} T \f] in-place. /// /// @param[in,out] u Data to transform. The shape is `(m, n)`, where /// `m` is the number of dgerees-of-freedom and the storage is /// row-major. /// @param[in] n Number of columns in `data`. /// @param[in] cell_info Permutation info for the cell. template void T_apply_right(std::span u, int n, std::uint32_t cell_info) const; /// @brief Right(post)-apply the inverse of the operator applied by /// T_apply(). /// /// Computes \f[ u^{T} \leftarrow u^{T} T^{-1} \f] in-place. /// /// @param[in,out] u Data to transform. The shape is `(m, n)`, where /// `m` is the number of dgerees-of-freedom and the storage is /// row-major. /// @param[in] n Number of columns in `data`. /// @param cell_info Permutation info for the cell. template void Tinv_apply_right(std::span u, int n, std::uint32_t cell_info) const; /// @brief Right(post)-apply the transpose inverse of the operator /// applied by T_apply(). /// /// Computes \f[ u^{T} \leftarrow u^{T} T^{-T} \f] in-place. /// /// @param[in,out] u Data to transform. The shape is `(m, n)`, where /// `m` is the number of dgerees-of-freedom and the storage is /// row-major. /// @param[in] n Number of columns in `data`. /// @param cell_info Permutation info for the cell. template void Tt_inv_apply_right(std::span u, int n, std::uint32_t cell_info) const; /// @brief Return the interpolation points. /// /// The interpolation points are the coordinates on the reference /// element where a function need to be evaluated in order to /// interpolate it in the finite element space. /// @return Array of coordinate with shape `(num_points, tdim)` const std::pair, std::array>& points() const { return _points; } /// @brief Return a matrix of weights interpolation. /// /// To interpolate a function in this finite element, the functions /// should be evaluated at each point given by points(). These /// function values should then be multiplied by the weight matrix to /// give the coefficients of the interpolated function. /// /// The shape of the returned matrix will be `(dim, num_points * /// value_size)`, where `dim` is the number of DOFs in the finite /// element, `num_points` is the number of points returned by /// points(), and `value_size` is the value size of the finite /// element. /// /// For example, to interpolate into a Lagrange space, the following /// should be done: /// \code{.pseudo} /// i_m = element.interpolation_matrix() /// pts = element.points() /// values = vector(pts.shape(0)) /// FOR i, p IN ENUMERATE(pts): /// values[i] = f.evaluate_at(p) /// coefficients = i_m * values /// \endcode /// /// To interpolate into a Raviart-Thomas space, the following should /// be done: /// \code{.pseudo} /// i_m = element.interpolation_matrix() /// pts = element.points() /// vs = prod(element.value_shape()) /// values = VECTOR(pts.shape(0) * vs) /// FOR i, p IN ENUMERATE(pts): /// values[i::pts.shape(0)] = f.evaluate_at(p) /// coefficients = i_m * values /// \endcode /// /// To interpolate into a Lagrange space with a block size, the /// following should be done: /// \code{.pseudo} /// i_m = element.interpolation_matrix() /// pts = element.points() /// coefficients = VECTOR(element.dim() * block_size) /// FOR b IN RANGE(block_size): /// values = vector(pts.shape(0)) /// FOR i, p IN ENUMERATE(pts): /// values[i] = f.evaluate_at(p)[b] /// coefficients[::block_size] = i_m * values /// \endcode /// /// @return The interpolation matrix. Shape is `(ndofs, number of /// interpolation points)`. const std::pair, std::array>& interpolation_matrix() const { return _matM; } /// @brief Get the dual matrix. /// /// This is the matrix @f$BD^{T}@f$, as described in the documentation /// of the FiniteElement() constructor. /// @return The dual matrix. Shape is `(ndofs, ndofs)` = `(dim(), dim())`. const std::pair, std::array>& dual_matrix() const { return _dual_matrix; } /// @brief Get the coefficients that define the polynomial set in /// terms of the orthonormal polynomials. /// /// The polynomials spanned by each finite element in Basix are /// represented as a linear combination of the orthonormal polynomials /// of a given degree on the cell. Each row of this matrix defines a /// polynomial in the set spanned by the finite element. /// /// For example, the orthonormal polynomials of degree <= 1 on a /// triangle are (where a, b, c, d are some constants): /// /// - (sqrt(2), 0) /// - (a*x - b, 0) /// - (c*y - d, 0) /// - (0, sqrt(2)) /// - (0, a*x - b) /// - (0, c*y - d) /// /// For a degree 1 Raviart-Thomas element, the first two rows of /// wcoeffs would be the following, as (1, 0) and (0, 1) are spanned /// by the element /// /// - [1, 0, 0, 0, 0, 0] /// - [0, 0, 0, 1, 0, 0] /// /// The third row of wcoeffs in this example would give coefficients /// that represent (x, y) in terms of the orthonormal polynomials: /// /// - [-b/(a*sqrt(2)), 1/a, 0, -d/(c*sqrt(2)), 0, 1/c] /// /// These coefficients are only stored for custom elements. This /// function will throw an exception if called on a non-custom /// element. /// /// @return Coefficient matrix. Shape is `(dim(finite element polyset), /// dim(Lagrange polynomials))`. const std::pair, std::array>& wcoeffs() const { return _wcoeffs; } /// @brief Get the interpolation points for each subentity. /// /// The indices of this data are `(tdim, entity index, point index, /// dim)`. const std::array< std::vector, std::array>>, 4>& x() const { return _x; } /// @brief Get the interpolation matrices for each subentity. /// /// The shape of this data is `(tdim, entity index, dof, value size, /// point_index, derivative)`. /// /// These matrices define how to evaluate the DOF functionals /// associated with each sub-entity of the cell. Given a function f, /// the functionals associated with the `e`-th entity of dimension `d` /// can be computed as follows: /// /// \code{.pseudo} /// matrix = element.M()[d][e] /// pts = element.x()[d][e] /// nderivs = element /// values = f.eval_derivs(nderivs, pts) /// result = ZEROS(matrix.shape(0)) /// FOR i IN RANGE(matrix.shape(0)): /// FOR j IN RANGE(matrix.shape(1)): /// FOR k IN RANGE(matrix.shape(2)): /// FOR l IN RANGE(matrix.shape(3)): /// result[i] += matrix[i, j, k, l] * values[l][k][j] /// \endcode /// /// For example, for a degree 1 Raviart-Thomas (RT) element on a triangle, the /// DOF functionals are integrals over the edges of the dot product of the /// function with the normal to the edge. In this case, `x()` would contain /// quadrature points for each edge, and `M()` would be a 1 by 2 by `npoints` /// by 1 array for each edge. For each point, the `[0, :, point, 0]` slice of /// this would be the quadrature weight multiplied by the normal. For all /// entities that are not edges, the entries in `x()` and `M()` for a degree 1 /// RT element would have size 0. /// /// These matrices are only stored for custom elements. This function will /// throw an exception if called on a non-custom element /// @return The interpolation matrices. The indices of this data are `(tdim, /// entity index, dof, vs, point_index, derivative)`. const std::array< std::vector, std::array>>, 4>& M() const { return _M; } /// @brief Get the matrix of coefficients. /// /// @return The coefficient matrix. Shape is `(ndofs, ndofs)`. const std::pair, std::array>& coefficient_matrix() const { return _coeffs; } /// @brief Indicates whether or not this element can be represented as a /// product of elements defined on lower-dimensional reference cells. /// /// If the product exists, this element's basis functions can be /// computed as a tensor product of the basis elements of the elements /// in the product. /// /// If such a factorisation exists, /// get_tensor_product_representation() can be used to get these /// elements. bool has_tensor_product_factorisation() const { return !_tensor_factors.empty(); } /// @brief Get the tensor product representation of this element. /// /// @throws std::runtime_error Thrown if no such factorisation exists. /// /// The tensor product representation will be a vector of vectors of /// finite elements. Each tuple contains a vector of finite elements, /// and a vector of integers. The vector of finite elements gives the /// elements on an interval that appear in the tensor product /// representation. The vector of integers gives the permutation /// between the numbering of the tensor product DOFs and the number of /// the DOFs of this Basix element. /// @return The tensor product representation std::vector>> get_tensor_product_representation() const { if (!has_tensor_product_factorisation()) throw std::runtime_error("Element has no tensor product representation."); return _tensor_factors; } /// @brief Indicates whether or not the interpolation matrix for this /// element is an identity matrix. /// @return True if the interpolation matrix is the identity and false /// otherwise. bool interpolation_is_identity() const { return _interpolation_is_identity; } /// @brief The number of derivatives needed when interpolating int interpolation_nderivs() const { return _interpolation_nderivs; } /// @brief Get dof layout const std::vector& dof_ordering() const { return _dof_ordering; } private: /// Data permutation /// @param data Data to be permuted /// @param block_size /// @param cell_info Cell bitmap selecting required permutations /// @param eperm Permutation to use /// @param post Whether reflect is pre- or post- rotation. template void permute_data( std::span data, int block_size, std::uint32_t cell_info, const std::map>>& eperm) const; using array2_t = std::pair, std::array>; using array3_t = std::pair, std::array>; using trans_data_t = std::vector, array2_t>>; /// Data transformation /// @param data Data to be transformed (using matrices) /// @param block_size /// @param cell_info Cell bitmap selecting required transforms /// @param etrans Transformation matrices /// @param post Whether reflect is pre- or post- rotation. template void transform_data(std::span data, int block_size, std::uint32_t cell_info, const std::map& etrans, OP op) const; // Cell type cell::type _cell_type; // Polyset type polyset::type _poly_type; // Topological dimension of the cell std::size_t _cell_tdim; // Topological dimension of the cell std::vector> _cell_subentity_types; // Finite element family element::family _family; // Lagrange variant element::lagrange_variant _lagrange_variant; // DPC variant element::dpc_variant _dpc_variant; // Degree that was input when creating the element int _degree; // Degree int _interpolation_nderivs; // Highest degree polynomial in element's polyset int _embedded_superdegree; // Highest degree space that is a subspace of element's polyset int _embedded_subdegree; // Value shape std::vector _value_shape; /// The mapping used to map this element from the reference to a cell maps::type _map_type; /// The Sobolev space this element is contained in sobolev::space _sobolev_space; // Shape function coefficient of expansion sets on cell. If shape // function is given by @f$\psi_i = \sum_{k} \phi_{k} // \alpha^{i}_{k}@f$, then _coeffs(i, j) = @f$\alpha^i_k@f$. ie // _coeffs.row(i) are the expansion coefficients for shape function i // (@f$\psi_{i}@f$). std::pair, std::array> _coeffs; // Dofs associated with each cell (sub-)entity std::vector>> _edofs; // Dofs associated with the closdure of each cell (sub-)entity std::vector>> _e_closure_dofs; // Entity transformations std::map _entity_transformations; // Set of points used for point evaluation // Experimental - currently used for an implementation of // "tabulate_dof_coordinates" Most useful for Lagrange. This may change or go // away. For non-Lagrange elements, these points will be used in combination // with _interpolation_matrix to perform interpolation std::pair, std::array> _points; // Interpolation points on the cell. The shape is (entity_dim, num // entities of given dimension, num_points, tdim) std::array, std::array>>, 4> _x; /// The interpolation weights and points std::pair, std::array> _matM; // Indicates whether or not the DOF transformations are all // permutations bool _dof_transformations_are_permutations; // Indicates whether or not the DOF transformations are all identity bool _dof_transformations_are_identity; // The entity permutations (factorised). This will only be set if // _dof_transformations_are_permutations is True and // _dof_transformations_are_identity is False std::map>> _eperm; // The reverse entity permutations (factorised). This will only be set // if _dof_transformations_are_permutations is True and // _dof_transformations_are_identity is False std::map>> _eperm_inv; // The entity transformations in precomputed form std::map _etrans; // The transposed entity transformations in precomputed form std::map _etransT; // The inverse entity transformations in precomputed form std::map _etrans_inv; // The inverse transpose entity transformations in precomputed form std::map _etrans_invT; // The subentity closure permutations (factorised). This will only be set if // _dof_transformations_are_permutations is True std::map>> _subentity_closure_perm; // The inverse subentity closure permutations (factorised). This will only be // set if _dof_transformations_are_permutations is True std::map>> _subentity_closure_perm_inv; // Indicates whether or not this is the discontinuous version of the // element bool _discontinuous; // The dual matrix std::pair, std::array> _dual_matrix; // Dof reordering for different element dof layout compatibility. // The reference basix layout is ordered by entity, i.e. dofs on // vertices, followed by edges, faces, then internal dofs. // _dof_ordering stores the map to the new order required, e.g. // for a P2 triangle, _dof_ordering=[0 3 5 1 2 4] will place // dofs 0, 3, 5 on the vertices and 1, 2, 4, on the edges. std::vector _dof_ordering; // Tensor product representation // Entries of tuple are (list of elements on an interval, permutation // of DOF numbers) // @todo: For vector-valued elements, a tensor product type and a // scaling factor may additionally be needed. std::vector> _tensor_factors; // Is the interpolation matrix an identity? bool _interpolation_is_identity; // The coefficients that define the polynomial set in terms of the // orthonormal polynomials std::pair, std::array> _wcoeffs; // Interpolation matrices for each entity using array4_t = std::vector, std::array>>; std::array _M; }; /// Create a custom finite element /// @param[in] cell_type The cell type /// @param[in] value_shape The value shape of the element /// @param[in] wcoeffs Matrices for the kth value index containing the /// expansion coefficients defining a polynomial basis spanning the /// polynomial space for this element. Shape is (dim(finite element polyset), /// dim(Legendre polynomials)) /// @param[in] x Interpolation points. Indices are (tdim, entity index, /// point index, dim) /// @param[in] M The interpolation matrices. Indices are (tdim, entity /// index, dof, vs, point_index, derivative) /// @param[in] interpolation_nderivs The number of derivatives that need to be /// used during interpolation /// @param[in] map_type The type of map to be used to map values from /// the reference to a cell /// @param[in] sobolev_space The underlying Sobolev space for the element /// @param[in] discontinuous Indicates whether or not this is the /// discontinuous version of the element /// @param[in] embedded_subdegree The highest degree n such that a /// Lagrange (or vector Lagrange) element of degree n is a subspace of this /// element /// @param[in] embedded_superdegree The degree of a polynomial in this element's /// polyset /// @param[in] poly_type The type of polyset to use for this element /// @return A custom finite element template FiniteElement create_custom_element( cell::type cell_type, const std::vector& value_shape, impl::mdspan_t wcoeffs, const std::array>, 4>& x, const std::array>, 4>& M, int interpolation_nderivs, maps::type map_type, sobolev::space sobolev_space, bool discontinuous, int embedded_subdegree, int embedded_superdegree, polyset::type poly_type); /// Create an element using a given Lagrange variant and a given DPC variant /// @param[in] family The element family /// @param[in] cell The reference cell type that the element is defined on /// @param[in] degree The degree of the element /// @param[in] lvariant The variant of Lagrange to use /// @param[in] dvariant The variant of DPC to use /// @param[in] discontinuous Indicates whether the element is discontinuous /// between cells points of the element. The discontinuous element will have the /// same DOFs, but they will all be associated with the interior of the cell. /// @param[in] dof_ordering Ordering of dofs for ElementDofLayout /// @return A finite element template FiniteElement create_element(element::family family, cell::type cell, int degree, element::lagrange_variant lvariant, element::dpc_variant dvariant, bool discontinuous, std::vector dof_ordering = {}); /// Get the tensor product DOF ordering for an element /// @param[in] family The element family /// @param[in] cell The reference cell type that the element is defined on. /// Currently limited to quadrilateral or hexahedron. /// @param[in] degree The degree of the element /// @param[in] lvariant The variant of Lagrange to use /// @param[in] dvariant The variant of DPC to use /// @param[in] discontinuous Indicates whether the element is discontinuous /// between cells points of the element. The discontinuous element will have the /// same DOFs, but they will all be associated with the interior of the cell. /// @return A vector containing the dof ordering std::vector tp_dof_ordering(element::family family, cell::type cell, int degree, element::lagrange_variant lvariant, element::dpc_variant dvariant, bool discontinuous); /// Get the tensor factors of an element /// @param[in] family The element family /// @param[in] cell The reference cell type that the element is defined on. /// Currently limited to quadrilateral or hexahedron. /// @param[in] degree The degree of the element /// @param[in] lvariant The variant of Lagrange to use /// @param[in] dvariant The variant of DPC to use /// @param[in] discontinuous Indicates whether the element is discontinuous /// between cells points of the element. The discontinuous element will have the /// same DOFs, but they will all be associated with the interior of the cell. /// @param[in] dof_ordering The ordering of the DOFs /// @return A list of lists of finite element factors template std::vector>> tp_factors(element::family family, cell::type cell, int degree, element::lagrange_variant lvariant, element::dpc_variant dvariant, bool discontinuous, std::vector dof_ordering); /// Create an element with Tensor Product dof ordering /// @param[in] family The element family /// @param[in] cell The reference cell type that the element is defined on. /// Currently limited to quadrilateral or hexahedron. /// @param[in] degree The degree of the element /// @param[in] lvariant The variant of Lagrange to use /// @param[in] dvariant The variant of DPC to use /// @param[in] discontinuous Indicates whether the element is discontinuous /// between cells points of the element. The discontinuous element will have the /// same DOFs, but they will all be associated with the interior of the cell. /// @return A finite element template FiniteElement create_tp_element(element::family family, cell::type cell, int degree, element::lagrange_variant lvariant, element::dpc_variant dvariant, bool discontinuous); /// Return the Basix version number /// @return version string std::string version(); //----------------------------------------------------------------------------- template template void FiniteElement::permute_data( std::span data, int block_size, std::uint32_t cell_info, const std::map>>& eperm) const { if (_cell_tdim >= 2) { // This assumes 3 bits are used per face. This will need updating if 3D // cells with faces with more than 4 sides are implemented int face_start = _cell_tdim == 3 ? 3 * _edofs[2].size() : 0; // Permute DOFs on edges { auto& trans = eperm.at(cell::type::interval)[0]; for (std::size_t e = 0; e < _edofs[1].size(); ++e) { // Reverse an edge if (cell_info >> (face_start + e) & 1) { precompute::apply_permutation_mapped(trans, data, _edofs[1][e], block_size); } } } if (_cell_tdim == 3) { // Permute DOFs on faces for (std::size_t f = 0; f < _edofs[2].size(); ++f) { auto& trans = eperm.at(_cell_subentity_types[2][f]); // Reflect a face (pre rotate) if (!post and cell_info >> (3 * f) & 1) { precompute::apply_permutation_mapped(trans[1], data, _edofs[2][f], block_size); } // Rotate a face for (std::uint32_t r = 0; r < (cell_info >> (3 * f + 1) & 3); ++r) { precompute::apply_permutation_mapped(trans[0], data, _edofs[2][f], block_size); } // Reflect a face (post rotate) if (post and cell_info >> (3 * f) & 1) { precompute::apply_permutation_mapped(trans[1], data, _edofs[2][f], block_size); } } } } } //----------------------------------------------------------------------------- template template void FiniteElement::transform_data( std::span data, int block_size, std::uint32_t cell_info, const std::map& etrans, OP op) const { if (_cell_tdim >= 2) { // This assumes 3 bits are used per face. This will need updating if // 3D cells with faces with more than 4 sides are implemented int face_start = _cell_tdim == 3 ? 3 * _edofs[2].size() : 0; int dofstart = 0; for (auto& edofs0 : _edofs[0]) dofstart += edofs0.size(); // Transform DOFs on edges { auto& [v_size_t, matrix] = etrans.at(cell::type::interval)[0]; for (std::size_t e = 0; e < _edofs[1].size(); ++e) { // Reverse an edge if (cell_info >> (face_start + e) & 1) { op(std::span(v_size_t), mdspan_t(matrix.first.data(), matrix.second), data, dofstart, block_size); } dofstart += _edofs[1][e].size(); } } if (_cell_tdim == 3) { // Permute DOFs on faces for (std::size_t f = 0; f < _edofs[2].size(); ++f) { auto& trans = etrans.at(_cell_subentity_types[2][f]); // Reflect a face (pre rotation) if (!post and cell_info >> (3 * f) & 1) { const auto& m = trans[1]; const auto& v_size_t = std::get<0>(m); const auto& matrix = std::get<1>(m); op(std::span(v_size_t), mdspan_t(matrix.first.data(), matrix.second), data, dofstart, block_size); } // Rotate a face for (std::uint32_t r = 0; r < (cell_info >> (3 * f + 1) & 3); ++r) { const auto& m = trans[0]; const auto& v_size_t = std::get<0>(m); const auto& matrix = std::get<1>(m); op(std::span(v_size_t), mdspan_t(matrix.first.data(), matrix.second), data, dofstart, block_size); } // Reflect a face (post rotation) if (post and cell_info >> (3 * f) & 1) { const auto& m = trans[1]; const auto& v_size_t = std::get<0>(m); const auto& matrix = std::get<1>(m); op(std::span(v_size_t), mdspan_t(matrix.first.data(), matrix.second), data, dofstart, block_size); } dofstart += _edofs[2][f].size(); } } } } //----------------------------------------------------------------------------- template template void FiniteElement::T_apply(std::span u, int n, std::uint32_t cell_info) const { if (_dof_transformations_are_identity) return; if (_dof_transformations_are_permutations) permute_data(u, n, cell_info, _eperm); else { transform_data(u, n, cell_info, _etrans, precompute::apply_matrix); } } //----------------------------------------------------------------------------- template template void FiniteElement::Tt_apply(std::span u, int n, std::uint32_t cell_info) const { if (_dof_transformations_are_identity) return; else if (_dof_transformations_are_permutations) permute_data(u, n, cell_info, _eperm_inv); else { transform_data(u, n, cell_info, _etransT, precompute::apply_matrix); } } //----------------------------------------------------------------------------- template template void FiniteElement::Tt_inv_apply(std::span u, int n, std::uint32_t cell_info) const { if (_dof_transformations_are_identity) return; else if (_dof_transformations_are_permutations) permute_data(u, n, cell_info, _eperm); else { transform_data(u, n, cell_info, _etrans_invT, precompute::apply_matrix); } } //----------------------------------------------------------------------------- template template void FiniteElement::Tinv_apply(std::span u, int n, std::uint32_t cell_info) const { if (_dof_transformations_are_identity) return; else if (_dof_transformations_are_permutations) permute_data(u, n, cell_info, _eperm_inv); else { transform_data(u, n, cell_info, _etrans_inv, precompute::apply_matrix); } } //----------------------------------------------------------------------------- template template void FiniteElement::Tt_apply_right(std::span u, int n, std::uint32_t cell_info) const { if (_dof_transformations_are_identity) return; else if (_dof_transformations_are_permutations) { assert(u.size() % n == 0); const int step = u.size() / n; for (int i = 0; i < n; ++i) { std::span dblock(u.data() + i * step, step); permute_data(dblock, 1, cell_info, _eperm); } } else { transform_data(u, n, cell_info, _etrans, precompute::apply_tranpose_matrix_right); } } //----------------------------------------------------------------------------- template template void FiniteElement::Tinv_apply_right(std::span u, int n, std::uint32_t cell_info) const { if (_dof_transformations_are_identity) return; else if (_dof_transformations_are_permutations) { assert(u.size() % n == 0); const int step = u.size() / n; for (int i = 0; i < n; ++i) { std::span dblock(u.data() + i * step, step); permute_data(dblock, 1, cell_info, _eperm); } } else { transform_data(u, n, cell_info, _etrans_invT, precompute::apply_tranpose_matrix_right); } } //----------------------------------------------------------------------------- template template void FiniteElement::T_apply_right(std::span u, int n, std::uint32_t cell_info) const { if (_dof_transformations_are_identity) return; else if (_dof_transformations_are_permutations) { assert(u.size() % n == 0); const int step = u.size() / n; for (int i = 0; i < n; ++i) { std::span dblock(u.data() + i * step, step); permute_data(dblock, 1, cell_info, _eperm_inv); } } else { transform_data(u, n, cell_info, _etransT, precompute::apply_tranpose_matrix_right); } } //----------------------------------------------------------------------------- template template void FiniteElement::Tt_inv_apply_right(std::span u, int n, std::uint32_t cell_info) const { if (_dof_transformations_are_identity) return; else if (_dof_transformations_are_permutations) { assert(u.size() % n == 0); const int step = u.size() / n; for (int i = 0; i < n; ++i) { std::span dblock(u.data() + i * step, step); permute_data(dblock, 1, cell_info, _eperm_inv); } } else { transform_data(u, n, cell_info, _etrans_inv, precompute::apply_tranpose_matrix_right); } } //----------------------------------------------------------------------------- } // namespace basix fenics-basix-0.9.0/cpp/basix/indexing.h000066400000000000000000000020621470517546000177520ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson // FEniCS Project // SPDX-License-Identifier: MIT #pragma once /// @brief Indexing. namespace basix::indexing { /// @brief Compute trivial indexing in a 1D array (for completeness). /// @param p Index in x /// @return 1D Index constexpr int idx(int p) { return p; } /// Compute indexing in a 2D triangular array compressed into a 1D /// array. This can be used to find the index of a derivative returned /// by FiniteElement::tabulate(). For instance to find d2N/dx2, use /// `FiniteElement::tabulate(2, points)[idx(2, 0)];` /// @param p Index in x /// @param q Index in y /// @return 1D Index constexpr int idx(int p, int q) { return (p + q + 1) * (p + q) / 2 + q; } /// @brief Compute indexing in a 3D tetrahedral array compressed into a /// 1D array. /// @param p Index in x. /// @param q Index in y. /// @param r Index in z. /// @return 1D Index. constexpr int idx(int p, int q, int r) { return (p + q + r) * (p + q + r + 1) * (p + q + r + 2) / 6 + (q + r) * (q + r + 1) / 2 + r; } } // namespace basix::indexing fenics-basix-0.9.0/cpp/basix/interpolation.cpp000066400000000000000000000101441470517546000213670ustar00rootroot00000000000000// Copyright (c) 2021 Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #include "interpolation.h" #include "finite-element.h" #include #include using namespace basix; namespace stdex = MDSPAN_IMPL_STANDARD_NAMESPACE::MDSPAN_IMPL_PROPOSED_NAMESPACE; template using mdspan_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>; //---------------------------------------------------------------------------- template std::pair, std::array> basix::compute_interpolation_operator(const FiniteElement& element_from, const FiniteElement& element_to) { if (element_from.cell_type() != element_to.cell_type()) { throw std::runtime_error( "Cannot interpolate between elements defined on different cell types."); } const auto [points, shape] = element_to.points(); const auto [tab_b, tab_shape] = element_from.tabulate(0, mdspan_t(points.data(), shape)); mdspan_t tab(tab_b.data(), tab_shape); const auto [imb, imshape] = element_to.interpolation_matrix(); mdspan_t i_m(imb.data(), imshape); const std::size_t dim_to = element_to.dim(); const std::size_t dim_from = element_from.dim(); const std::size_t npts = tab.extent(1); const std::size_t vs_from = std::accumulate(element_from.value_shape().begin(), element_from.value_shape().end(), 1, std::multiplies{}); const std::size_t vs_to = std::reduce(element_to.value_shape().begin(), element_to.value_shape().end(), 1, std::multiplies{}); if (vs_from != vs_to) { if (vs_to == 1) { // Map element_from's components into element_to std::array shape = {dim_to * vs_from, dim_from}; std::vector outb(shape[0] * shape[1], 0.0); mdspan_t out(outb.data(), shape); for (std::size_t i = 0; i < vs_from; ++i) for (std::size_t j = 0; j < dim_to; ++j) for (std::size_t k = 0; k < dim_from; ++k) for (std::size_t l = 0; l < npts; ++l) out(i + j * vs_from, k) += i_m(j, l) * tab(0, l, k, i); return {std::move(outb), std::move(shape)}; } else if (vs_from == 1) { // Map duplicates of element_to to components of element_to std::array shape = {dim_to, dim_from * vs_to}; std::vector outb(shape[0] * shape[1], 0.0); mdspan_t out(outb.data(), shape); for (std::size_t i = 0; i < vs_to; ++i) for (std::size_t j = 0; j < dim_from; ++j) for (std::size_t k = 0; k < dim_to; ++k) for (std::size_t l = 0; l < npts; ++l) out(k, i + j * vs_to) += i_m(k, i * npts + l) * tab(0, l, j, 0); return {std::move(outb), std::move(shape)}; } else { throw std::runtime_error("Cannot interpolate between elements with this " "combination of value sizes."); } } else { std::array shape = {dim_to, dim_from}; std::vector outb(shape[0] * shape[1], 0.0); mdspan_t out(outb.data(), shape); for (std::size_t i = 0; i < dim_to; ++i) for (std::size_t j = 0; j < dim_from; ++j) for (std::size_t k = 0; k < vs_from; ++k) for (std::size_t l = 0; l < npts; ++l) out(i, j) += i_m(i, k * npts + l) * tab(0, l, j, k); return {std::move(outb), std::move(shape)}; } } //---------------------------------------------------------------------------- /// @cond template std::pair, std::array> basix::compute_interpolation_operator(const FiniteElement&, const FiniteElement&); template std::pair, std::array> basix::compute_interpolation_operator(const FiniteElement&, const FiniteElement&); /// @endcond //----------------------------------------------------------------------------- fenics-basix-0.9.0/cpp/basix/interpolation.h000066400000000000000000000037421470517546000210420ustar00rootroot00000000000000// Copyright (c) 2021 Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #pragma once #include #include #include #include namespace basix { template class FiniteElement; /// @brief Compute a matrix that represents the interpolation between /// two elements. /// /// If the two elements have the same value size, this function returns /// the interpolation between them. /// /// If element_from has value size 1 and element_to has value size > 1, /// then this function returns a matrix to interpolate from a blocked /// element_from (ie multiple copies of element_from) into element_to. /// /// If element_to has value size 1 and element_from has value size > 1, /// then this function returns a matrix that interpolates the components /// of element_from into copies of element_to. /// /// @note If the elements have different value sizes and both are /// greater than 1, this function throws a runtime error /// /// In order to interpolate functions between finite element spaces on /// arbitrary cells, the functions must be pulled back to the reference /// element (this pull back includes applying DOF transformations). The /// matrix that this function returns can then be applied, then the /// result pushed forward to the cell. If element_from and element_to /// have the same map type, then only the DOF transformations need to be /// applied, as the pull back and push forward cancel each other out. /// /// @param[in] element_from The element to interpolate from /// @param[in] element_to The element to interpolate to /// @return Matrix operator that maps the 'from' degrees-of-freedom to /// the 'to' degrees-of-freedom. Shape is (ndofs(element_to), /// ndofs(element_from)) template std::pair, std::array> compute_interpolation_operator(const FiniteElement& element_from, const FiniteElement& element_to); } // namespace basix fenics-basix-0.9.0/cpp/basix/lattice.cpp000066400000000000000000000677031470517546000201420ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson, Garth N. Wells, and Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #include "lattice.h" #include "cell.h" #include "math.h" #include "polyset.h" #include "quadrature.h" #include #include #include #include #include #include using namespace basix; namespace stdex = MDSPAN_IMPL_STANDARD_NAMESPACE::MDSPAN_IMPL_PROPOSED_NAMESPACE; namespace { //----------------------------------------------------------------------------- template std::vector linspace(T x0, T x1, std::size_t n) { if (n == 0) return {}; else if (n == 1) return {x0}; else { std::vector p(n, x0); p.back() = x1; const T delta = (x1 - x0) / (n - 1); for (std::size_t i = 1; i < p.size() - 1; ++i) p[i] += i * delta; return p; } } //----------------------------------------------------------------------------- template std::vector create_interval_equispaced(std::size_t n, bool exterior) { const T h = exterior ? 0 : 1.0 / static_cast(n); const std::size_t num_pts = exterior ? n + 1 : n - 1; return linspace(h, 1.0 - h, num_pts); } //----------------------------------------------------------------------------- template std::vector create_interval_gll(std::size_t n, bool exterior) { if (n == 0) return {0.5}; else { const std::vector pts = quadrature::get_gll_points(n + 1); const std::size_t b = exterior ? 0 : 1; std::vector x(n + 1 - 2 * b); if (exterior) { x[0] = pts[0]; x[n] = pts[1]; } for (std::size_t j = 2; j < n + 1; ++j) x[j - 1 - b] = pts[j]; return x; } } //----------------------------------------------------------------------------- template std::vector create_interval_chebyshev(std::size_t n, bool exterior) { if (exterior) { throw std::runtime_error( "Chebyshev points including endpoints are not supported."); } std::vector x(n - 1); for (std::size_t i = 1; i < n; ++i) x[i - 1] = 0.5 - std::cos((2 * i - 1) * M_PI / (2 * n - 2)) / 2.0; return x; } //----------------------------------------------------------------------------- template std::vector create_interval_gl(std::size_t n, bool exterior) { if (exterior) { throw std::runtime_error( "GL points including endpoints are not supported."); } if (n == 0) return {0.5}; else return quadrature::get_gl_points(n - 1); } //----------------------------------------------------------------------------- template std::vector create_interval_gl_plus_endpoints(std::size_t n, bool exterior) { std::vector x_gl = create_interval_gl(n, false); if (!exterior) return x_gl; else { std::vector x(n + 1); x[0] = 0.0; x[n] = 1.0; for (std::size_t i = 0; i < n - 1; ++i) x[i + 1] = x_gl[i]; return x; } } //----------------------------------------------------------------------------- template std::vector create_interval_chebyshev_plus_endpoints(std::size_t n, bool exterior) { std::vector x_cheb = create_interval_chebyshev(n, false); if (!exterior) return x_cheb; else { std::vector x(n + 1); x[0] = 0.0; x[n] = 1.0; for (std::size_t i = 0; i < n - 1; ++i) x[i + 1] = x_cheb[i]; return x; } } //----------------------------------------------------------------------------- template std::vector create_interval(std::size_t n, lattice::type lattice_type, bool exterior) { if (n == 0) return {0.5}; else { switch (lattice_type) { case lattice::type::equispaced: return create_interval_equispaced(n, exterior); case lattice::type::gll: return create_interval_gll(n, exterior); case lattice::type::chebyshev: return create_interval_chebyshev(n, exterior); case lattice::type::gl: return create_interval_gl(n, exterior); case lattice::type::chebyshev_plus_endpoints: return create_interval_chebyshev_plus_endpoints(n, exterior); case lattice::type::gl_plus_endpoints: return create_interval_gl_plus_endpoints(n, exterior); default: throw std::runtime_error("Unrecognised lattice type."); } } } //----------------------------------------------------------------------------- template stdex::mdarray> tabulate_dlagrange(std::size_t n, std::span x) { std::vector equi_pts(n + 1); for (std::size_t i = 0; i < equi_pts.size(); ++i) equi_pts[i] = static_cast(i) / static_cast(n); MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> equi_pts_v(equi_pts.data(), n + 1, 1); const auto [dual_values_b, dshape] = polyset::tabulate( cell::type::interval, polyset::type::standard, n, 0, equi_pts_v); MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> dual_values(dual_values_b.data(), dshape); std::vector dualmat_b(dual_values.extent(1) * dual_values.extent(2)); MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> dualmat(dualmat_b.data(), dual_values.extent(1), dual_values.extent(2)); for (std::size_t i = 0; i < dualmat.extent(0); ++i) for (std::size_t j = 0; j < dualmat.extent(1); ++j) dualmat(i, j) = dual_values(0, i, j); using cmdspan2_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::extents< std::size_t, MDSPAN_IMPL_STANDARD_NAMESPACE::dynamic_extent, 1>>; const auto [tabulated_values_b, tshape] = polyset::tabulate(cell::type::interval, polyset::type::standard, n, 0, cmdspan2_t(x.data(), x.size(), 1)); MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> tabulated_values(tabulated_values_b.data(), tshape); std::vector tabulated_b(tabulated_values.extent(1) * tabulated_values.extent(2)); MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> tabulated(tabulated_b.data(), tabulated_values.extent(1), tabulated_values.extent(2)); for (std::size_t i = 0; i < tabulated.extent(0); ++i) for (std::size_t j = 0; j < tabulated.extent(1); ++j) tabulated(i, j) = tabulated_values(0, i, j); std::vector c = math::solve(dualmat, tabulated); return stdex::mdarray< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>( tabulated.extents(), std::move(c)); } //----------------------------------------------------------------------------- template std::vector warp_function(lattice::type lattice_type, int n, std::span x) { std::vector pts = create_interval(n, lattice_type, true); for (int i = 0; i < n + 1; ++i) pts[i] -= static_cast(i) / static_cast(n); stdex::mdarray> v = tabulate_dlagrange(n, x); std::vector w(v.extent(1), 0); for (std::size_t i = 0; i < v.extent(0); ++i) for (std::size_t j = 0; j < v.extent(1); ++j) w[j] += v(i, j) * pts[i]; return w; } //----------------------------------------------------------------------------- template std::pair, std::array> create_quad(std::size_t n, lattice::type lattice_type, bool exterior) { if (n == 0) return {{0.5, 0.5}, {1, 2}}; else { const std::vector r = create_interval(n, lattice_type, exterior); const std::size_t m = r.size(); std::array shape = {m * m, 2}; std::vector xb(shape[0] * shape[1]); MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::extents< std::size_t, MDSPAN_IMPL_STANDARD_NAMESPACE::dynamic_extent, 2>> x(xb.data(), shape); std::size_t c = 0; for (std::size_t j = 0; j < m; ++j) { for (std::size_t i = 0; i < m; ++i) { x(c, 0) = r[i]; x(c, 1) = r[j]; c++; } } return {std::move(xb), std::move(shape)}; } } //----------------------------------------------------------------------------- template std::pair, std::array> create_hex(int n, lattice::type lattice_type, bool exterior) { if (n == 0) return {{0.5, 0.5, 0.5}, {1, 3}}; else { const std::vector r = create_interval(n, lattice_type, exterior); const std::size_t m = r.size(); std::array shape = {m * m * m, 3}; std::vector xb(shape[0] * shape[1]); MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::extents< std::size_t, MDSPAN_IMPL_STANDARD_NAMESPACE::dynamic_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::dynamic_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::dynamic_extent, 3>> x(xb.data(), m, m, m, 3); for (std::size_t k = 0; k < m; ++k) { for (std::size_t j = 0; j < m; ++j) { for (std::size_t i = 0; i < m; ++i) { x(k, j, i, 0) = r[i]; x(k, j, i, 1) = r[j]; x(k, j, i, 2) = r[k]; } } } return {std::move(xb), std::move(shape)}; } } //----------------------------------------------------------------------------- template std::pair, std::array> create_tri_equispaced(std::size_t n, bool exterior) { const std::size_t b = exterior ? 0 : 1; std::array shape = {(n - 3 * b + 1) * (n - 3 * b + 2) / 2, 2}; std::vector _p(shape[0] * shape[1]); MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::extents< std::size_t, MDSPAN_IMPL_STANDARD_NAMESPACE::dynamic_extent, 2>> p(_p.data(), shape); // Displacement from GLL points in 1D, scaled by 1 /(r * (1 - r)) std::vector r = linspace(0.0, 1.0, 2 * n + 1); int c = 0; for (std::size_t j = b; j < (n - b + 1); ++j) { for (std::size_t i = b; i < (n - b + 1 - j); ++i) { p(c, 0) = r[2 * i]; p(c, 1) = r[2 * j]; ++c; } } return {std::move(_p), std::move(shape)}; } //----------------------------------------------------------------------------- /// Warp points: see Hesthaven and Warburton, Nodal Discontinuous /// Galerkin Methods, pp. 175-180, /// https://doi.org/10.1007/978-0-387-72067-8_6 template std::pair, std::array> create_tri_warped(std::size_t n, lattice::type lattice_type, bool exterior) { const std::size_t b = exterior ? 0 : 1; // Points std::array shape = {(n - 3 * b + 1) * (n - 3 * b + 2) / 2, 2}; std::vector _p(shape[0] * shape[1]); MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::extents< std::size_t, MDSPAN_IMPL_STANDARD_NAMESPACE::dynamic_extent, 2>> p(_p.data(), shape); // Displacement from GLL points in 1D, scaled by 1 /(r * (1 - r)) const std::vector r = linspace(0.0, 1.0, 2 * n + 1); std::vector wbar = warp_function(lattice_type, n, r); for (std::size_t i = 1; i < 2 * n - 1; ++i) wbar[i] /= r[i] * (1.0 - r[i]); int c = 0; for (std::size_t j = b; j < (n - b + 1); ++j) { for (std::size_t i = b; i < (n - b + 1 - j); ++i) { const T x = r[2 * i]; const T y = r[2 * j]; p(c, 0) = x; p(c, 1) = y; const std::size_t l = n - j - i; const T a = r[2 * l]; p(c, 0) += x * (a * wbar[n + i - l] + y * wbar[n + i - j]); p(c, 1) += y * (a * wbar[n + j - l] + x * wbar[n + j - i]); ++c; } } return {std::move(_p), std::move(shape)}; } //----------------------------------------------------------------------------- template std::vector isaac_point(lattice::type lattice_type, std::span a) { if (a.size() == 1) return {1}; else { std::vector res(a.size(), 0); T denominator = 0; std::vector sub_a(std::next(a.begin()), a.end()); const std::size_t size = std::reduce(a.begin(), a.end()); std::vector x = create_interval(size, lattice_type, true); for (std::size_t i = 0; i < a.size(); ++i) { if (i > 0) sub_a[i - 1] = a[i - 1]; const std::size_t sub_size = size - a[i]; const std::vector sub_res = isaac_point(lattice_type, sub_a); for (std::size_t j = 0; j < sub_res.size(); ++j) res[j < i ? j : j + 1] += x[sub_size] * sub_res[j]; denominator += x[sub_size]; } std::ranges::for_each(res, [denominator](auto& x) { x /= denominator; }); return res; } } //----------------------------------------------------------------------------- /// Warp points, See: Isaac, Recursive, Parameter-Free, Explicitly /// Defined Interpolation Nodes for Simplices, /// http://dx.doi.org/10.1137/20M1321802. template std::pair, std::array> create_tri_isaac(std::size_t n, lattice::type lattice_type, bool exterior) { const std::size_t b = exterior ? 0 : 1; std::array shape = {(n - 3 * b + 1) * (n - 3 * b + 2) / 2, 2}; std::vector _p(shape[0] * shape[1]); MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::extents< std::size_t, MDSPAN_IMPL_STANDARD_NAMESPACE::dynamic_extent, 2>> p(_p.data(), shape); int c = 0; for (std::size_t j = b; j < (n - b + 1); ++j) { for (std::size_t i = b; i < (n - b + 1 - j); ++i) { const std::vector isaac_p = isaac_point(lattice_type, std::array{i, j, n - i - j}); for (std::size_t k = 0; k < 2; ++k) p(c, k) = isaac_p[k]; ++c; } } return {std::move(_p), std::move(shape)}; } //----------------------------------------------------------------------------- /// See: Blyth, and Pozrikidis, A Lobatto interpolation grid over the /// triangle, https://dx.doi.org/10.1093/imamat/hxh077 template std::pair, std::array> create_tri_centroid(std::size_t n, lattice::type lattice_type, bool exterior) { if (exterior) { throw std::runtime_error( "Centroid method not implemented to include boundaries"); } std::array shape = {(n - 2) * (n - 1) / 2, 2}; std::vector _p(shape[0] * shape[1]); MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::extents< std::size_t, MDSPAN_IMPL_STANDARD_NAMESPACE::dynamic_extent, 2>> p(_p.data(), shape); const std::vector x = create_interval(n, lattice_type, false); int c = 0; for (std::size_t i = 0; i + 1 < x.size(); ++i) { const T xi = x[i]; for (std::size_t j = 0; j + i + 1 < x.size(); ++j) { const T xj = x[j]; const T xk = x[i + j + 1]; p(c, 0) = (2 * xj + xk - xi) / 3; p(c, 1) = (2 * xi + xk - xj) / 3; ++c; } } return {std::move(_p), std::move(shape)}; } //----------------------------------------------------------------------------- template std::pair, std::array> create_tri(std::size_t n, lattice::type lattice_type, bool exterior, lattice::simplex_method simplex_method) { if (n == 0) return {{1.0 / 3.0, 1.0 / 3.0}, {1, 2}}; else if (lattice_type == lattice::type::equispaced) return create_tri_equispaced(n, exterior); else { switch (simplex_method) { case lattice::simplex_method::warp: return create_tri_warped(n, lattice_type, exterior); case lattice::simplex_method::isaac: return create_tri_isaac(n, lattice_type, exterior); case lattice::simplex_method::centroid: return create_tri_centroid(n, lattice_type, exterior); case lattice::simplex_method::none: { // Methods will all agree when n <= 3 if (n <= 3) return create_tri_warped(n, lattice_type, exterior); else { throw std::runtime_error( "A simplex type must be given to create points on a triangle."); } } default: throw std::runtime_error("Unrecognised simplex type."); } } } //----------------------------------------------------------------------------- template std::pair, std::array> create_tet_equispaced(std::size_t n, bool exterior) { const std::size_t b = exterior ? 0 : 1; const std::vector r = linspace(0.0, 1.0, 2 * n + 1); std::array shape = {(n - 4 * b + 1) * (n - 4 * b + 2) * (n - 4 * b + 3) / 6, 3}; std::vector xb(shape[0] * shape[1]); MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::extents< std::size_t, MDSPAN_IMPL_STANDARD_NAMESPACE::dynamic_extent, 3>> x(xb.data(), shape); std::size_t c = 0; for (std::size_t k = b; k < (n - b + 1); ++k) { for (std::size_t j = b; j < (n - b + 1 - k); ++j) { for (std::size_t i = b; i < (n - b + 1 - j - k); ++i) { x(c, 0) = r[2 * i]; x(c, 1) = r[2 * j]; x(c, 2) = r[2 * k]; ++c; } } } return {std::move(xb), std::move(shape)}; } //----------------------------------------------------------------------------- /// See: Isaac, Recursive, Parameter-Free, Explicitly Defined Interpolation /// Nodes for Simplices http://dx.doi.org/10.1137/20M1321802 template std::pair, std::array> create_tet_isaac(std::size_t n, lattice::type lattice_type, bool exterior) { const std::size_t b = exterior ? 0 : 1; std::array shape = {(n - 4 * b + 1) * (n - 4 * b + 2) * (n - 4 * b + 3) / 6, 3}; std::vector xb(shape[0] * shape[1]); MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::extents< std::size_t, MDSPAN_IMPL_STANDARD_NAMESPACE::dynamic_extent, 3>> x(xb.data(), shape); int c = 0; for (std::size_t k = b; k < (n - b + 1); ++k) { for (std::size_t j = b; j < (n - b + 1 - k); ++j) { for (std::size_t i = b; i < (n - b + 1 - j - k); ++i) { const std::vector ip = isaac_point(lattice_type, std::array{i, j, k, n - i - j - k}); for (std::size_t l = 0; l < 3; ++l) x(c, l) = ip[l]; ++c; } } } return {std::move(xb), std::move(shape)}; } //----------------------------------------------------------------------------- template std::pair, std::array> create_tet_warped(std::size_t n, lattice::type lattice_type, bool exterior) { const std::size_t b = exterior ? 0 : 1; const std::vector r = linspace(0.0, 1.0, 2 * n + 1); std::vector wbar = warp_function(lattice_type, n, r); std::transform(std::next(r.begin()), std::prev(r.end()), std::next(wbar.begin()), std::next(wbar.begin()), [](auto r, auto w) { return w / (r * (1.0 - r)); }); std::array shape = {(n - 4 * b + 1) * (n - 4 * b + 2) * (n - 4 * b + 3) / 6, 3}; std::vector xb(shape[0] * shape[1]); MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::extents< std::size_t, MDSPAN_IMPL_STANDARD_NAMESPACE::dynamic_extent, 3>> p(xb.data(), shape); std::size_t c = 0; for (std::size_t k = b; k < (n - b + 1); ++k) { for (std::size_t j = b; j < (n - b + 1 - k); ++j) { for (std::size_t i = b; i < (n - b + 1 - j - k); ++i) { const std::size_t l = n - k - j - i; const T x = r[2 * i]; const T y = r[2 * j]; const T z = r[2 * k]; const T a = r[2 * l]; p(c, 0) = x; p(c, 1) = y; p(c, 2) = z; const T dx = x * (a * wbar[n + i - l] + y * wbar[n + i - j] + z * wbar[n + i - k]); const T dy = y * (a * wbar[n + j - l] + z * wbar[n + j - k] + x * wbar[n + j - i]); const T dz = z * (a * wbar[n + k - l] + x * wbar[n + k - i] + y * wbar[n + k - j]); p(c, 0) += dx; p(c, 1) += dy; p(c, 2) += dz; ++c; } } } return {std::move(xb), std::move(shape)}; } //----------------------------------------------------------------------------- /// See: Blyth, and Pozrikidis, A Lobatto interpolation grid over the /// triangle, https://dx.doi.org/10.1093/imamat/hxh077 template std::pair, std::array> create_tet_centroid(std::size_t n, lattice::type lattice_type, bool exterior) { if (exterior) { throw std::runtime_error( "Centroid method not implemented to include boundaries"); } const std::vector x = create_interval(n, lattice_type, false); std::array shape = {(n - 3) * (n - 2) * (n - 1) / 6, 3}; std::vector xb(shape[0] * shape[1]); MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::extents< std::size_t, MDSPAN_IMPL_STANDARD_NAMESPACE::dynamic_extent, 3>> p(xb.data(), shape); int c = 0; for (std::size_t i = 0; i + 2 < x.size(); ++i) { const T xi = x[i]; for (std::size_t j = 0; j + i + 2 < x.size(); ++j) { const T xj = x[j]; for (std::size_t k = 0; k + j + i + 2 < x.size(); ++k) { const T xk = x[k]; const T xl = x[i + j + k + 2]; p(c, 0) = (3 * xk + xl - xi - xj) / 4; p(c, 1) = (3 * xj + xl - xi - xk) / 4; p(c, 2) = (3 * xi + xl - xj - xk) / 4; ++c; } } } return {std::move(xb), std::move(shape)}; } //----------------------------------------------------------------------------- template std::pair, std::array> create_tet(std::size_t n, lattice::type lattice_type, bool exterior, lattice::simplex_method simplex_method) { if (n == 0) return {{0.25, 0.25, 0.25}, {1, 3}}; else if (lattice_type == lattice::type::equispaced) return create_tet_equispaced(n, exterior); else { switch (simplex_method) { case lattice::simplex_method::warp: return create_tet_warped(n, lattice_type, exterior); case lattice::simplex_method::isaac: return create_tet_isaac(n, lattice_type, exterior); case lattice::simplex_method::centroid: return create_tet_centroid(n, lattice_type, exterior); case lattice::simplex_method::none: { // Methods will all agree when n <= 3 if (n <= 3) return create_tet_warped(n, lattice_type, exterior); else { throw std::runtime_error( "A simplex type must be given to create points on a triangle."); } } default: throw std::runtime_error("Unrecognised simplex type."); } } } //----------------------------------------------------------------------------- template std::pair, std::array> create_prism(std::size_t n, lattice::type lattice_type, bool exterior, lattice::simplex_method simplex_method) { using cmdspan22_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::extents< std::size_t, MDSPAN_IMPL_STANDARD_NAMESPACE::dynamic_extent, 2>>; using mdspan23_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::extents< std::size_t, MDSPAN_IMPL_STANDARD_NAMESPACE::dynamic_extent, 3>>; if (n == 0) return {{1.0 / 3.0, 1.0 / 3.0, 0.5}, {1, 3}}; else { const auto [tri_pts_b, trishape] = create_tri(n, lattice_type, exterior, simplex_method); cmdspan22_t tri_pts(tri_pts_b.data(), trishape); const std::vector line_pts = create_interval(n, lattice_type, exterior); std::array shape = {tri_pts.extent(0) * line_pts.size(), 3}; std::vector xb(shape[0] * shape[1]); mdspan23_t x(xb.data(), shape); for (std::size_t i = 0; i < line_pts.size(); ++i) for (std::size_t j = 0; j < tri_pts.extent(0); ++j) for (std::size_t k = 0; k < 2; ++k) x(i * tri_pts.extent(0) + j, k) = tri_pts(j, k); for (std::size_t i = 0; i < line_pts.size(); ++i) { for (std::size_t j = i * tri_pts.extent(0); j < (i + 1) * tri_pts.extent(0); ++j) { x(j, 2) = line_pts[i]; } } return {std::move(xb), std::move(shape)}; } } //----------------------------------------------------------------------------- template std::pair, std::array> create_pyramid_equispaced(int n, bool exterior) { const T h = 1.0 / static_cast(n); const std::size_t b = (exterior == false) ? 1 : 0; n -= b * 3; const std::size_t m = (n + 1) * (n + 2) * (2 * n + 3) / 6; std::array shape = {m, 3}; std::vector xb(shape[0] * shape[1]); MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::extents< std::size_t, MDSPAN_IMPL_STANDARD_NAMESPACE::dynamic_extent, 3>> x(xb.data(), shape); int c = 0; for (int k = 0; k < n + 1; ++k) { for (int j = 0; j < n + 1 - k; ++j) { for (int i = 0; i < n + 1 - k; ++i) { x(c, 0) = h * (i + b); x(c, 1) = h * (j + b); x(c, 2) = h * (k + b); c++; } } } return {std::move(xb), std::move(shape)}; } //----------------------------------------------------------------------------- template std::pair, std::array> create_pyramid(int n, lattice::type lattice_type, bool exterior, lattice::simplex_method /*simplex_method*/) { if (n == 0) return {{0.4, 0.4, 0.2}, {1, 3}}; else if (n <= 2 || lattice_type == lattice::type::equispaced) return create_pyramid_equispaced(n, exterior); else { throw std::runtime_error( "Non-equispaced points on pyramids not supported yet."); } } } // namespace //----------------------------------------------------------------------------- template std::pair, std::array> lattice::create(cell::type celltype, int n, lattice::type type, bool exterior, lattice::simplex_method simplex_method) { switch (celltype) { case cell::type::point: return {{0.0}, {1, 1}}; case cell::type::interval: { auto x = create_interval(n, type, exterior); return {x, {x.size(), 1}}; } case cell::type::triangle: return create_tri(n, type, exterior, simplex_method); case cell::type::tetrahedron: return create_tet(n, type, exterior, simplex_method); case cell::type::quadrilateral: return create_quad(n, type, exterior); case cell::type::hexahedron: return create_hex(n, type, exterior); case cell::type::prism: return create_prism(n, type, exterior, simplex_method); case cell::type::pyramid: return create_pyramid(n, type, exterior, simplex_method); default: throw std::runtime_error("Unsupported cell for lattice"); } } //----------------------------------------------------------------------------- /// @cond // Explicit instantiation for double and float template std::pair, std::array> lattice::create(cell::type, int, lattice::type, bool, lattice::simplex_method); template std::pair, std::array> lattice::create(cell::type, int, lattice::type, bool, lattice::simplex_method); /// @endcond //----------------------------------------------------------------------------- fenics-basix-0.9.0/cpp/basix/lattice.h000066400000000000000000000062211470517546000175730ustar00rootroot00000000000000// Copyright (c) 2020-2022 Chris Richardson and Garth N. Wells // FEniCS Project // SPDX-License-Identifier: MIT #pragma once #include "cell.h" #include #include #include #include /// @brief Lattices of points namespace basix::lattice { /// @brief The type of point spacing to be used in a lattice. /// /// @note type::chebyshev_plus_endpoints() and type::gl_plus_endpoints() are /// only intended for internal use only. enum class type { equispaced = 0, /*!< Equally spaced points */ gll = 1, /*!< Gauss-Lobatto-Legendre (GLL) points */ chebyshev = 2, /*!< Chebyshev points */ gl = 4, /*!< Gauss-Legendre (GL) points */ chebyshev_plus_endpoints = 10, /*!< Chebyshev points plus the endpoints of the interval */ gl_plus_endpoints = 11, /*!< Gauss-Legendre (GL) points plus the endpoints of the interval */ }; /// @brief The method used to generate points inside simplices. enum class simplex_method { none = 0, /*!< Used when no method is needed, e.g. when making points on a quadrilateral, or when making equispaced points). */ warp = 1, /*!< Warping from Hesthaven and Warburton, Nodal Discontinuous Galerkin Methods, https://doi.org/10.1007/978-0-387-72067-8. */ isaac = 2, /*!< Points described in Isaac, Recursive, Parameter-Free, Explicitly Defined Interpolation Nodes for Simplices, https://doi.org/10.1137/20M1321802. */ centroid = 3, /*!< Place points at the centroids of the grid created by putting points on the edges, as described in Blyth and Pozrikidis, A Lobatto interpolation grid over the triangle, https://doi.org/10.1093/imamat/hxh077. */ }; /// @brief Create a lattice of points on a reference cell optionally /// including the outer surface points. /// /// For a given `celltype`, this creates a set of points on a regular /// grid which covers the cell, eg for a quadrilateral, with n=2, the /// points are: `[0,0], [0.5,0], [1,0], [0,0.5], [0.5,0.5], [1,0.5], /// [0,1], [0.5,1], [1,1]`. If the parameter exterior is set to false, /// the points lying on the external boundary are omitted, in this case /// for a quadrilateral with `n == 2`, the points are: `[0.5, 0.5]`. The /// lattice type can be chosen as type::equispaced or type::gll. The /// type::gll lattice has points spaced along each edge at the /// Gauss-Lobatto-Legendre quadrature points. These are the same as /// type::equispaced when `n < 3`. /// /// @param celltype The cell type. /// @param n Size in each direction. There are `n + 1` points along each /// edge of the cell. /// @param type A lattice type. /// @param exterior If set, includes outer boundaries. /// @param simplex_method The method used to generate points on /// simplices. /// @return Set of points. Shape is `(npoints, tdim)` and storage is /// row-major. template std::pair, std::array> create(cell::type celltype, int n, lattice::type type, bool exterior, lattice::simplex_method simplex_method = lattice::simplex_method::none); } // namespace basix::lattice fenics-basix-0.9.0/cpp/basix/maps.h000066400000000000000000000124001470517546000171020ustar00rootroot00000000000000// Copyright (c) 2021-2022 Matthew Scroggs and Garth N. Wells // FEniCS Project // SPDX-License-Identifier: MIT #pragma once #include "mdspan.hpp" #include #include #include /// Information about finite element maps namespace basix::maps { namespace impl { /// @private These structs are used to get the float/value type from a /// template argument, including support for complex types. template struct scalar_value_type { /// @internal typedef T value_type; }; /// @private template struct scalar_value_type> { typedef typename T::value_type value_type; }; /// @private Convenience typedef template using scalar_value_type_t = typename scalar_value_type::value_type; } // namespace impl /// Map type enum class type { identity = 0, L2Piola = 1, covariantPiola = 2, contravariantPiola = 3, doubleCovariantPiola = 4, doubleContravariantPiola = 5, }; /// @brief L2 Piola map template void l2_piola(O&& r, const P& U, const Q& /*J*/, double detJ, const R& /*K*/) { assert(U.extent(0) == r.extent(0)); assert(U.extent(1) == r.extent(1)); for (std::size_t i = 0; i < U.extent(0); ++i) for (std::size_t j = 0; j < U.extent(1); ++j) r(i, j) = U(i, j) / detJ; } /// @brief Covariant Piola map template void covariant_piola(O&& r, const P& U, const Q& /*J*/, double /*detJ*/, const R& K) { using T = typename std::decay_t::value_type; using Z = typename impl::scalar_value_type_t; for (std::size_t p = 0; p < U.extent(0); ++p) { // r_p = K^T U_p, where p indicates the p-th row for (std::size_t i = 0; i < r.extent(1); ++i) { T acc = 0; for (std::size_t k = 0; k < K.extent(0); ++k) acc += static_cast(K(k, i)) * U(p, k); r(p, i) = acc; } } } /// @brief Contravariant Piola map template void contravariant_piola(O&& r, const P& U, const Q& J, double detJ, const R& /*K*/) { using T = typename std::decay_t::value_type; using Z = typename impl::scalar_value_type_t; for (std::size_t p = 0; p < U.extent(0); ++p) { for (std::size_t i = 0; i < r.extent(1); ++i) { T acc = 0; for (std::size_t k = 0; k < J.extent(1); ++k) acc += static_cast(J(i, k)) * U(p, k); r(p, i) = acc; } } std::transform(r.data_handle(), r.data_handle() + r.size(), r.data_handle(), [detJ](auto ri) { return ri / static_cast(detJ); }); } /// @brief Double covariant Piola map template void double_covariant_piola(O&& r, const P& U, const Q& J, double /*detJ*/, const R& K) { namespace stdex = MDSPAN_IMPL_STANDARD_NAMESPACE::MDSPAN_IMPL_PROPOSED_NAMESPACE; using T = typename std::decay_t::value_type; using Z = typename impl::scalar_value_type_t; for (std::size_t p = 0; p < U.extent(0); ++p) { MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> _U(U.data_handle() + p * U.extent(1), J.extent(1), J.extent(1)); MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> _r(r.data_handle() + p * r.extent(1), K.extent(1), K.extent(1)); // _r = K^T _U K for (std::size_t i = 0; i < _r.extent(0); ++i) { for (std::size_t j = 0; j < _r.extent(1); ++j) { T acc = 0; for (std::size_t k = 0; k < K.extent(0); ++k) for (std::size_t l = 0; l < _U.extent(1); ++l) acc += static_cast(K(k, i)) * _U(k, l) * static_cast(K(l, j)); _r(i, j) = acc; } } } } /// @brief Double contravariant Piola map template void double_contravariant_piola(O&& r, const P& U, const Q& J, double detJ, const R& /*K*/) { namespace stdex = MDSPAN_IMPL_STANDARD_NAMESPACE::MDSPAN_IMPL_PROPOSED_NAMESPACE; using T = typename std::decay_t::value_type; using Z = typename impl::scalar_value_type_t; for (std::size_t p = 0; p < U.extent(0); ++p) { MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> _U(U.data_handle() + p * U.extent(1), J.extent(1), J.extent(1)); MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> _r(r.data_handle() + p * r.extent(1), J.extent(0), J.extent(0)); // _r = J U J^T for (std::size_t i = 0; i < _r.extent(0); ++i) { for (std::size_t j = 0; j < _r.extent(1); ++j) { T acc = 0; for (std::size_t k = 0; k < J.extent(1); ++k) for (std::size_t l = 0; l < _U.extent(1); ++l) acc += static_cast(J(i, k)) * _U(k, l) * static_cast(J(j, l)); _r(i, j) = acc; } } } std::transform(r.data_handle(), r.data_handle() + r.size(), r.data_handle(), [detJ](auto ri) { return ri / static_cast(detJ * detJ); }); } } // namespace basix::maps fenics-basix-0.9.0/cpp/basix/math.h000066400000000000000000000307741470517546000171110ustar00rootroot00000000000000// Copyright (C) 2021 Igor Baratta // // This file is part of DOLFINx (https://www.fenicsproject.org) // // SPDX-License-Identifier: LGPL-3.0-or-later #pragma once #include #include #include #include #include #include #include #include #include "mdspan.hpp" extern "C" { void ssyevd_(char* jobz, char* uplo, int* n, float* a, int* lda, float* w, float* work, int* lwork, int* iwork, int* liwork, int* info); void dsyevd_(char* jobz, char* uplo, int* n, double* a, int* lda, double* w, double* work, int* lwork, int* iwork, int* liwork, int* info); void sgesv_(int* N, int* NRHS, float* A, int* LDA, int* IPIV, float* B, int* LDB, int* INFO); void dgesv_(int* N, int* NRHS, double* A, int* LDA, int* IPIV, double* B, int* LDB, int* INFO); void sgemm_(char* transa, char* transb, int* m, int* n, int* k, float* alpha, float* a, int* lda, float* b, int* ldb, float* beta, float* c, int* ldc); void dgemm_(char* transa, char* transb, int* m, int* n, int* k, double* alpha, double* a, int* lda, double* b, int* ldb, double* beta, double* c, int* ldc); int sgetrf_(const int* m, const int* n, float* a, const int* lda, int* lpiv, int* info); int dgetrf_(const int* m, const int* n, double* a, const int* lda, int* lpiv, int* info); } /// @brief Mathematical functions. /// /// @note The functions in this namespace are designed to be called /// multiple times at runtime, so their performance is critical. namespace basix::math { namespace impl { /// @brief Compute C = A * B using BLAS. /// @param[in] A Input matrix /// @param[in] B Input matrix /// @return A * B template void dot_blas(std::span A, std::array Ashape, std::span B, std::array Bshape, std::span C) { static_assert(std::is_same_v or std::is_same_v); assert(Ashape[1] == Bshape[0]); assert(C.size() == Ashape[0] * Bshape[1]); int M = Ashape[0]; int N = Bshape[1]; int K = Ashape[1]; T alpha = 1; T beta = 0; int lda = K; int ldb = N; int ldc = N; char trans = 'N'; if constexpr (std::is_same_v) { sgemm_(&trans, &trans, &N, &M, &K, &alpha, const_cast(B.data()), &ldb, const_cast(A.data()), &lda, &beta, C.data(), &ldc); } else if constexpr (std::is_same_v) { dgemm_(&trans, &trans, &N, &M, &K, &alpha, const_cast(B.data()), &ldb, const_cast(A.data()), &lda, &beta, C.data(), &ldc); } } } // namespace impl /// @brief Compute the outer product of vectors u and v. /// @param u The first vector /// @param v The second vector /// @return The outer product. The type will be the same as `u`. template std::pair, std::array> outer(const U& u, const V& v) { std::vector result(u.size() * v.size()); for (std::size_t i = 0; i < u.size(); ++i) for (std::size_t j = 0; j < v.size(); ++j) result[i * v.size() + j] = u[i] * v[j]; return {std::move(result), {u.size(), v.size()}}; } /// Compute the cross product u x v /// @param u The first vector. It must has size 3. /// @param v The second vector. It must has size 3. /// @return The cross product `u x v`. The type will be the same as `u`. template std::array cross(const U& u, const V& v) { assert(u.size() == 3); assert(v.size() == 3); return {u[1] * v[2] - u[2] * v[1], u[2] * v[0] - u[0] * v[2], u[0] * v[1] - u[1] * v[0]}; } /// Compute the eigenvalues and eigenvectors of a square Hermitian matrix A /// @param[in] A Input matrix, row-major storage /// @param[in] n Number of rows /// @return Eigenvalues (0) and eigenvectors (1). The eigenvector array /// uses column-major storage, which each column being an eigenvector. /// @pre The matrix `A` must be symmetric template std::pair, std::vector> eigh(std::span A, std::size_t n) { // Copy A std::vector M(A.begin(), A.end()); // Allocate storage for eigenvalues std::vector w(n, 0); int N = n; char jobz = 'V'; // Compute eigenvalues and eigenvectors char uplo = 'L'; // Lower int ldA = n; int lwork = -1; int liwork = -1; int info; std::vector work(1); std::vector iwork(1); // Query optimal workspace size if constexpr (std::is_same_v) { ssyevd_(&jobz, &uplo, &N, M.data(), &ldA, w.data(), work.data(), &lwork, iwork.data(), &liwork, &info); } else if constexpr (std::is_same_v) { dsyevd_(&jobz, &uplo, &N, M.data(), &ldA, w.data(), work.data(), &lwork, iwork.data(), &liwork, &info); } if (info != 0) throw std::runtime_error("Could not find workspace size for syevd."); // Solve eigen problem work.resize(work[0]); iwork.resize(iwork[0]); lwork = work.size(); liwork = iwork.size(); if constexpr (std::is_same_v) { ssyevd_(&jobz, &uplo, &N, M.data(), &ldA, w.data(), work.data(), &lwork, iwork.data(), &liwork, &info); } else if constexpr (std::is_same_v) { dsyevd_(&jobz, &uplo, &N, M.data(), &ldA, w.data(), work.data(), &lwork, iwork.data(), &liwork, &info); } if (info != 0) throw std::runtime_error("Eigenvalue computation did not converge."); return {std::move(w), std::move(M)}; } /// @brief Solve A X = B. /// @param[in] A The matrix /// @param[in] B Right-hand side matrix/vector /// @return A^{-1} B template std::vector solve(MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> A, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> B) { namespace stdex = MDSPAN_IMPL_STANDARD_NAMESPACE::MDSPAN_IMPL_PROPOSED_NAMESPACE; // Copy A and B to column-major storage stdex::mdarray, MDSPAN_IMPL_STANDARD_NAMESPACE::layout_left> _A(A.extents()), _B(B.extents()); for (std::size_t i = 0; i < A.extent(0); ++i) for (std::size_t j = 0; j < A.extent(1); ++j) _A(i, j) = A(i, j); for (std::size_t i = 0; i < B.extent(0); ++i) for (std::size_t j = 0; j < B.extent(1); ++j) _B(i, j) = B(i, j); int N = _A.extent(0); int nrhs = _B.extent(1); int lda = _A.extent(0); int ldb = _B.extent(0); // Pivot indices that define the permutation matrix for the LU solver std::vector piv(N); int info; if constexpr (std::is_same_v) sgesv_(&N, &nrhs, _A.data(), &lda, piv.data(), _B.data(), &ldb, &info); else if constexpr (std::is_same_v) dgesv_(&N, &nrhs, _A.data(), &lda, piv.data(), _B.data(), &ldb, &info); if (info != 0) throw std::runtime_error("Call to dgesv failed: " + std::to_string(info)); // Copy result to row-major storage std::vector rb(_B.extent(0) * _B.extent(1)); MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> r(rb.data(), _B.extents()); for (std::size_t i = 0; i < _B.extent(0); ++i) for (std::size_t j = 0; j < _B.extent(1); ++j) r(i, j) = _B(i, j); return rb; } /// @brief Check if A is a singular matrix, /// @param[in] A The matrix /// @return A bool indicating if the matrix is singular template bool is_singular( MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> A) { // Copy to column major matrix namespace stdex = MDSPAN_IMPL_STANDARD_NAMESPACE::MDSPAN_IMPL_PROPOSED_NAMESPACE; stdex::mdarray, MDSPAN_IMPL_STANDARD_NAMESPACE::layout_left> _A(A.extents()); for (std::size_t i = 0; i < A.extent(0); ++i) for (std::size_t j = 0; j < A.extent(1); ++j) _A(i, j) = A(i, j); std::vector B(A.extent(1), 1); int N = _A.extent(0); int nrhs = 1; int lda = _A.extent(0); int ldb = B.size(); // Pivot indices that define the permutation matrix for the LU solver std::vector piv(N); int info; if constexpr (std::is_same_v) sgesv_(&N, &nrhs, _A.data(), &lda, piv.data(), B.data(), &ldb, &info); else if constexpr (std::is_same_v) dgesv_(&N, &nrhs, _A.data(), &lda, piv.data(), B.data(), &ldb, &info); if (info < 0) { throw std::runtime_error("dgesv failed due to invalid value: " + std::to_string(info)); } else if (info > 0) return true; else return false; } /// @brief Compute the LU decomposition of the transpose of a square /// matrix A. /// @param[in,out] A The matrix /// @return The LU permutation, in prepared format (see /// precompute::prepare_permutation) template std::vector transpose_lu(std::pair, std::array>& A) { std::size_t dim = A.second[0]; assert(dim == A.second[1]); int N = dim; int info; std::vector lu_perm(dim); // Comput LU decomposition of M if constexpr (std::is_same_v) sgetrf_(&N, &N, A.first.data(), &N, lu_perm.data(), &info); else if constexpr (std::is_same_v) dgetrf_(&N, &N, A.first.data(), &N, lu_perm.data(), &info); if (info != 0) { throw std::runtime_error("LU decomposition failed: " + std::to_string(info)); } std::vector perm(dim); for (std::size_t i = 0; i < dim; ++i) perm[i] = static_cast(lu_perm[i] - 1); return perm; } /// @brief Compute C = A * B. /// @param[in] A Input matrix /// @param[in] B Input matrix /// @param[out] C Output matrix. Must be sized correctly before calling /// this function. template void dot(const U& A, const V& B, W&& C) { assert(A.extent(1) == B.extent(0)); assert(C.extent(0) == A.extent(0)); assert(C.extent(1) == B.extent(1)); if (A.extent(0) * B.extent(1) * A.extent(1) < 512) { std::fill_n(C.data_handle(), C.extent(0) * C.extent(1), 0); for (std::size_t i = 0; i < A.extent(0); ++i) for (std::size_t j = 0; j < B.extent(1); ++j) for (std::size_t k = 0; k < A.extent(1); ++k) C(i, j) += A(i, k) * B(k, j); } else { using T = typename std::decay_t::value_type; impl::dot_blas( std::span(A.data_handle(), A.size()), {A.extent(0), A.extent(1)}, std::span(B.data_handle(), B.size()), {B.extent(0), B.extent(1)}, std::span(C.data_handle(), C.size())); } } /// @brief Build an identity matrix. /// @param[in] n The number of rows/columns /// @return Identity matrix using row-major storage template std::vector eye(std::size_t n) { std::vector I(n * n, 0); namespace stdex = MDSPAN_IMPL_STANDARD_NAMESPACE::MDSPAN_IMPL_PROPOSED_NAMESPACE; MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> Iview(I.data(), n, n); for (std::size_t i = 0; i < n; ++i) Iview(i, i) = 1; return I; } /// @brief Orthogonalise the rows of a matrix (in place). /// @param[in] wcoeffs The matrix /// @param[in] start The row to start from. The rows before this should /// already be orthogonal. template void orthogonalise( MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> wcoeffs, std::size_t start = 0) { for (std::size_t i = start; i < wcoeffs.extent(0); ++i) { T norm = 0; for (std::size_t k = 0; k < wcoeffs.extent(1); ++k) norm += wcoeffs(i, k) * wcoeffs(i, k); norm = std::sqrt(norm); if (norm < 2 * std::numeric_limits::epsilon()) { throw std::runtime_error( "Cannot orthogonalise the rows of a matrix with incomplete row rank"); } for (std::size_t k = 0; k < wcoeffs.extent(1); ++k) wcoeffs(i, k) /= norm; for (std::size_t j = i + 1; j < wcoeffs.extent(0); ++j) { T a = 0; for (std::size_t k = 0; k < wcoeffs.extent(1); ++k) a += wcoeffs(i, k) * wcoeffs(j, k); for (std::size_t k = 0; k < wcoeffs.extent(1); ++k) wcoeffs(j, k) -= a * wcoeffs(i, k); } } } } // namespace basix::math fenics-basix-0.9.0/cpp/basix/mdspan.hpp000066400000000000000000007575261470517546000200150ustar00rootroot00000000000000#ifndef _MDSPAN_SINGLE_HEADER_INCLUDE_GUARD_ #define _MDSPAN_SINGLE_HEADER_INCLUDE_GUARD_ //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/mdarray //@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // See https://kokkos.org/LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER #ifndef MDSPAN_IMPL_STANDARD_NAMESPACE #define MDSPAN_IMPL_STANDARD_NAMESPACE std #endif #ifndef MDSPAN_IMPL_PROPOSED_NAMESPACE #define MDSPAN_IMPL_PROPOSED_NAMESPACE experimental #endif //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/mdspan //@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // See https://kokkos.org/LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER #ifndef MDSPAN_IMPL_STANDARD_NAMESPACE #define MDSPAN_IMPL_STANDARD_NAMESPACE std #endif #ifndef MDSPAN_IMPL_PROPOSED_NAMESPACE #define MDSPAN_IMPL_PROPOSED_NAMESPACE experimental #endif //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/mdspan/mdspan.hpp //@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // See https://kokkos.org/LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER #ifndef MDSPAN_HPP_ #define MDSPAN_HPP_ #ifndef MDSPAN_IMPL_STANDARD_NAMESPACE #define MDSPAN_IMPL_STANDARD_NAMESPACE Kokkos #endif #ifndef MDSPAN_IMPL_PROPOSED_NAMESPACE #define MDSPAN_IMPL_PROPOSED_NAMESPACE Experimental #endif //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/default_accessor.hpp //@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // See https://kokkos.org/LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/macros.hpp //@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // See https://kokkos.org/LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/config.hpp //@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // See https://kokkos.org/LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER #ifndef __has_include # define __has_include(x) 0 #endif #if __has_include() # include #else # include # include #endif #ifdef _MSVC_LANG #define _MDSPAN_CPLUSPLUS _MSVC_LANG #else #define _MDSPAN_CPLUSPLUS __cplusplus #endif #define MDSPAN_CXX_STD_14 201402L #define MDSPAN_CXX_STD_17 201703L #define MDSPAN_CXX_STD_20 202002L // Note GCC has not updated this in version 13 #ifdef __clang__ #define MDSPAN_CXX_STD_23 202302L #else #define MDSPAN_CXX_STD_23 202100L #endif #define MDSPAN_HAS_CXX_14 (_MDSPAN_CPLUSPLUS >= MDSPAN_CXX_STD_14) #define MDSPAN_HAS_CXX_17 (_MDSPAN_CPLUSPLUS >= MDSPAN_CXX_STD_17) #define MDSPAN_HAS_CXX_20 (_MDSPAN_CPLUSPLUS >= MDSPAN_CXX_STD_20) #define MDSPAN_HAS_CXX_23 (_MDSPAN_CPLUSPLUS >= MDSPAN_CXX_STD_23) static_assert(_MDSPAN_CPLUSPLUS >= MDSPAN_CXX_STD_14, "mdspan requires C++14 or later."); #ifndef _MDSPAN_COMPILER_CLANG # if defined(__clang__) # define _MDSPAN_COMPILER_CLANG __clang__ # endif #endif #if !defined(_MDSPAN_COMPILER_MSVC) && !defined(_MDSPAN_COMPILER_MSVC_CLANG) # if defined(_MSC_VER) # if !defined(_MDSPAN_COMPILER_CLANG) # define _MDSPAN_COMPILER_MSVC _MSC_VER # else # define _MDSPAN_COMPILER_MSVC_CLANG _MSC_VER # endif # endif #endif #ifndef _MDSPAN_COMPILER_INTEL # ifdef __INTEL_COMPILER # define _MDSPAN_COMPILER_INTEL __INTEL_COMPILER # endif #endif #ifndef _MDSPAN_COMPILER_APPLECLANG # ifdef __apple_build_version__ # define _MDSPAN_COMPILER_APPLECLANG __apple_build_version__ # endif #endif #ifndef _MDSPAN_HAS_CUDA # if defined(__CUDACC__) # define _MDSPAN_HAS_CUDA __CUDACC__ # endif #endif #ifndef _MDSPAN_HAS_HIP # if defined(__HIPCC__) # define _MDSPAN_HAS_HIP __HIPCC__ # endif #endif #ifndef _MDSPAN_HAS_SYCL # if defined(SYCL_LANGUAGE_VERSION) # define _MDSPAN_HAS_SYCL SYCL_LANGUAGE_VERSION # endif #endif #ifndef __has_cpp_attribute # define __has_cpp_attribute(x) 0 #endif #ifndef _MDSPAN_PRESERVE_STANDARD_LAYOUT // Preserve standard layout by default, but we're not removing the old version // that turns this off until we're sure this doesn't have an unreasonable cost // to the compiler or optimizer. # define _MDSPAN_PRESERVE_STANDARD_LAYOUT 1 #endif #if !defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) # if ((__has_cpp_attribute(no_unique_address) >= 201803L) && \ (!defined(__NVCC__) || MDSPAN_HAS_CXX_20) && \ (!defined(_MDSPAN_COMPILER_MSVC) || MDSPAN_HAS_CXX_20)) # define _MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS 1 # define _MDSPAN_NO_UNIQUE_ADDRESS [[no_unique_address]] # else # define _MDSPAN_NO_UNIQUE_ADDRESS # endif #endif // NVCC older than 11.6 chokes on the no-unique-address-emulation // so just pretend to use it (to avoid the full blown EBO workaround // which NVCC also doesn't like ...), and leave the macro empty #ifndef _MDSPAN_NO_UNIQUE_ADDRESS # if defined(__NVCC__) # define _MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS 1 # define _MDSPAN_USE_FAKE_ATTRIBUTE_NO_UNIQUE_ADDRESS # endif # define _MDSPAN_NO_UNIQUE_ADDRESS #endif // AMDs HIP compiler seems to have issues with concepts // it pretends concepts exist, but doesn't ship #ifndef __HIPCC__ #ifndef _MDSPAN_USE_CONCEPTS # if defined(__cpp_concepts) && __cpp_concepts >= 201507L # define _MDSPAN_USE_CONCEPTS 1 # endif #endif #endif #ifndef _MDSPAN_USE_FOLD_EXPRESSIONS # if (defined(__cpp_fold_expressions) && __cpp_fold_expressions >= 201603L) \ || (!defined(__cpp_fold_expressions) && MDSPAN_HAS_CXX_17) # define _MDSPAN_USE_FOLD_EXPRESSIONS 1 # endif #endif #ifndef _MDSPAN_USE_INLINE_VARIABLES # if defined(__cpp_inline_variables) && __cpp_inline_variables >= 201606L \ || (!defined(__cpp_inline_variables) && MDSPAN_HAS_CXX_17) # define _MDSPAN_USE_INLINE_VARIABLES 1 # endif #endif #ifndef _MDSPAN_NEEDS_TRAIT_VARIABLE_TEMPLATE_BACKPORTS # if (!(defined(__cpp_lib_type_trait_variable_templates) && __cpp_lib_type_trait_variable_templates >= 201510L) \ || !MDSPAN_HAS_CXX_17) # if !(defined(_MDSPAN_COMPILER_APPLECLANG) && MDSPAN_HAS_CXX_17) # define _MDSPAN_NEEDS_TRAIT_VARIABLE_TEMPLATE_BACKPORTS 1 # endif # endif #endif #ifndef _MDSPAN_USE_VARIABLE_TEMPLATES # if (defined(__cpp_variable_templates) && __cpp_variable_templates >= 201304 && MDSPAN_HAS_CXX_17) \ || (!defined(__cpp_variable_templates) && MDSPAN_HAS_CXX_17) # define _MDSPAN_USE_VARIABLE_TEMPLATES 1 # endif #endif // _MDSPAN_USE_VARIABLE_TEMPLATES #ifndef _MDSPAN_USE_CONSTEXPR_14 # if (defined(__cpp_constexpr) && __cpp_constexpr >= 201304) \ || (!defined(__cpp_constexpr) && MDSPAN_HAS_CXX_14) \ && (!(defined(__INTEL_COMPILER) && __INTEL_COMPILER <= 1700)) # define _MDSPAN_USE_CONSTEXPR_14 1 # endif #endif #ifndef _MDSPAN_USE_INTEGER_SEQUENCE # if defined(_MDSPAN_COMPILER_MSVC) # if (defined(__cpp_lib_integer_sequence) && __cpp_lib_integer_sequence >= 201304) # define _MDSPAN_USE_INTEGER_SEQUENCE 1 # endif # endif #endif #ifndef _MDSPAN_USE_INTEGER_SEQUENCE # if (defined(__cpp_lib_integer_sequence) && __cpp_lib_integer_sequence >= 201304) \ || (!defined(__cpp_lib_integer_sequence) && MDSPAN_HAS_CXX_14) \ /* as far as I can tell, libc++ seems to think this is a C++11 feature... */ \ || (defined(__GLIBCXX__) && __GLIBCXX__ > 20150422 && __GNUC__ < 5 && !defined(__INTEL_CXX11_MODE__)) // several compilers lie about integer_sequence working properly unless the C++14 standard is used # define _MDSPAN_USE_INTEGER_SEQUENCE 1 # elif defined(_MDSPAN_COMPILER_APPLECLANG) && MDSPAN_HAS_CXX_14 // appleclang seems to be missing the __cpp_lib_... macros, but doesn't seem to lie about C++14 making // integer_sequence work # define _MDSPAN_USE_INTEGER_SEQUENCE 1 # endif #endif #ifndef _MDSPAN_USE_RETURN_TYPE_DEDUCTION # if (defined(__cpp_return_type_deduction) && __cpp_return_type_deduction >= 201304) \ || (!defined(__cpp_return_type_deduction) && MDSPAN_HAS_CXX_14) # define _MDSPAN_USE_RETURN_TYPE_DEDUCTION 1 # endif #endif #ifndef _MDSPAN_USE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION # if (!defined(__NVCC__) || (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__ * 10 >= 1170)) && \ ((defined(__cpp_deduction_guides) && __cpp_deduction_guides >= 201703) || \ (!defined(__cpp_deduction_guides) && MDSPAN_HAS_CXX_17)) # define _MDSPAN_USE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION 1 # endif #endif #ifndef _MDSPAN_USE_STANDARD_TRAIT_ALIASES # if (defined(__cpp_lib_transformation_trait_aliases) && __cpp_lib_transformation_trait_aliases >= 201304) \ || (!defined(__cpp_lib_transformation_trait_aliases) && MDSPAN_HAS_CXX_14) # define _MDSPAN_USE_STANDARD_TRAIT_ALIASES 1 # elif defined(_MDSPAN_COMPILER_APPLECLANG) && MDSPAN_HAS_CXX_14 // appleclang seems to be missing the __cpp_lib_... macros, but doesn't seem to lie about C++14 # define _MDSPAN_USE_STANDARD_TRAIT_ALIASES 1 # endif #endif #ifndef _MDSPAN_DEFAULTED_CONSTRUCTORS_INHERITANCE_WORKAROUND # ifdef __GNUC__ # if __GNUC__ < 9 # define _MDSPAN_DEFAULTED_CONSTRUCTORS_INHERITANCE_WORKAROUND 1 # endif # endif #endif #ifndef MDSPAN_CONDITIONAL_EXPLICIT # if MDSPAN_HAS_CXX_20 # define MDSPAN_CONDITIONAL_EXPLICIT(COND) explicit(COND) # else # define MDSPAN_CONDITIONAL_EXPLICIT(COND) # endif #endif #ifndef MDSPAN_USE_BRACKET_OPERATOR # if defined(__cpp_multidimensional_subscript) # define MDSPAN_USE_BRACKET_OPERATOR 1 # else # define MDSPAN_USE_BRACKET_OPERATOR 0 # endif #endif #ifndef MDSPAN_USE_PAREN_OPERATOR # if !MDSPAN_USE_BRACKET_OPERATOR # define MDSPAN_USE_PAREN_OPERATOR 1 # else # define MDSPAN_USE_PAREN_OPERATOR 0 # endif #endif #if MDSPAN_USE_BRACKET_OPERATOR # define __MDSPAN_OP(mds,...) mds[__VA_ARGS__] // Corentins demo compiler for subscript chokes on empty [] call, // though I believe the proposal supports it? #ifdef MDSPAN_NO_EMPTY_BRACKET_OPERATOR # define __MDSPAN_OP0(mds) mds.accessor().access(mds.data_handle(),0) #else # define __MDSPAN_OP0(mds) mds[] #endif # define __MDSPAN_OP1(mds, a) mds[a] # define __MDSPAN_OP2(mds, a, b) mds[a,b] # define __MDSPAN_OP3(mds, a, b, c) mds[a,b,c] # define __MDSPAN_OP4(mds, a, b, c, d) mds[a,b,c,d] # define __MDSPAN_OP5(mds, a, b, c, d, e) mds[a,b,c,d,e] # define __MDSPAN_OP6(mds, a, b, c, d, e, f) mds[a,b,c,d,e,f] #else # define __MDSPAN_OP(mds,...) mds(__VA_ARGS__) # define __MDSPAN_OP0(mds) mds() # define __MDSPAN_OP1(mds, a) mds(a) # define __MDSPAN_OP2(mds, a, b) mds(a,b) # define __MDSPAN_OP3(mds, a, b, c) mds(a,b,c) # define __MDSPAN_OP4(mds, a, b, c, d) mds(a,b,c,d) # define __MDSPAN_OP5(mds, a, b, c, d, e) mds(a,b,c,d,e) # define __MDSPAN_OP6(mds, a, b, c, d, e, f) mds(a,b,c,d,e,f) #endif //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/config.hpp #include #include #include // std::is_void #if defined(_MDSPAN_HAS_CUDA) || defined(_MDSPAN_HAS_HIP) || defined(_MDSPAN_HAS_SYCL) #include "assert.h" #endif #ifndef _MDSPAN_HOST_DEVICE # if defined(_MDSPAN_HAS_CUDA) || defined(_MDSPAN_HAS_HIP) # define _MDSPAN_HOST_DEVICE __host__ __device__ # else # define _MDSPAN_HOST_DEVICE # endif #endif #ifndef MDSPAN_FORCE_INLINE_FUNCTION # ifdef _MDSPAN_COMPILER_MSVC // Microsoft compilers # define MDSPAN_FORCE_INLINE_FUNCTION __forceinline _MDSPAN_HOST_DEVICE # else # define MDSPAN_FORCE_INLINE_FUNCTION __attribute__((always_inline)) _MDSPAN_HOST_DEVICE # endif #endif #ifndef MDSPAN_INLINE_FUNCTION # define MDSPAN_INLINE_FUNCTION inline _MDSPAN_HOST_DEVICE #endif #ifndef MDSPAN_FUNCTION # define MDSPAN_FUNCTION _MDSPAN_HOST_DEVICE #endif #ifdef _MDSPAN_HAS_HIP # define MDSPAN_DEDUCTION_GUIDE _MDSPAN_HOST_DEVICE #else # define MDSPAN_DEDUCTION_GUIDE #endif // In CUDA defaulted functions do not need host device markup #ifndef MDSPAN_INLINE_FUNCTION_DEFAULTED # define MDSPAN_INLINE_FUNCTION_DEFAULTED #endif //============================================================================== // {{{1 #define MDSPAN_PP_COUNT(...) \ _MDSPAN_PP_INTERNAL_EXPAND_ARGS_PRIVATE( \ _MDSPAN_PP_INTERNAL_ARGS_AUGMENTER(__VA_ARGS__) \ ) #define _MDSPAN_PP_INTERNAL_ARGS_AUGMENTER(...) unused, __VA_ARGS__ #define _MDSPAN_PP_INTERNAL_EXPAND(x) x #define _MDSPAN_PP_INTERNAL_EXPAND_ARGS_PRIVATE(...) \ _MDSPAN_PP_INTERNAL_EXPAND( \ _MDSPAN_PP_INTERNAL_COUNT_PRIVATE( \ __VA_ARGS__, 69, 68, 67, 66, 65, 64, 63, 62, 61, \ 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, \ 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, \ 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, \ 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, \ 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 \ ) \ ) # define _MDSPAN_PP_INTERNAL_COUNT_PRIVATE( \ _1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, _9_, \ _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, \ _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, \ _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, \ _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, \ _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, \ _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, \ _70, count, ...) count \ /**/ #define MDSPAN_PP_STRINGIFY_IMPL(x) #x #define MDSPAN_PP_STRINGIFY(x) MDSPAN_PP_STRINGIFY_IMPL(x) #define MDSPAN_PP_CAT_IMPL(x, y) x ## y #define MDSPAN_PP_CAT(x, y) MDSPAN_PP_CAT_IMPL(x, y) #define MDSPAN_PP_EVAL(X, ...) X(__VA_ARGS__) #define MDSPAN_PP_REMOVE_PARENS_IMPL(...) __VA_ARGS__ #define MDSPAN_PP_REMOVE_PARENS(...) MDSPAN_PP_REMOVE_PARENS_IMPL __VA_ARGS__ #define MDSPAN_IMPL_STANDARD_NAMESPACE_STRING MDSPAN_PP_STRINGIFY(MDSPAN_IMPL_STANDARD_NAMESPACE) #define MDSPAN_IMPL_PROPOSED_NAMESPACE_STRING MDSPAN_PP_STRINGIFY(MDSPAN_IMPL_STANDARD_NAMESPACE) "::" MDSPAN_PP_STRINGIFY(MDSPAN_IMPL_PROPOSED_NAMESPACE) namespace MDSPAN_IMPL_STANDARD_NAMESPACE { namespace detail { #if defined(_MDSPAN_HAS_CUDA) || defined(_MDSPAN_HAS_HIP) MDSPAN_FUNCTION inline void default_precondition_violation_handler(const char* cond, const char* file, unsigned line) { printf("%s:%u: precondition failure: `%s`\n", file, line, cond); assert(0); } #elif defined(_MDSPAN_HAS_SYCL) MDSPAN_FUNCTION inline void default_precondition_violation_handler(const char* cond, const char* file, unsigned line) { sycl::ext::oneapi::experimental::printf("%s:%u: precondition failure: `%s`\n", file, line, cond); assert(0); } #else MDSPAN_FUNCTION inline void default_precondition_violation_handler(const char* cond, const char* file, unsigned line) { std::fprintf(stderr, "%s:%u: precondition failure: `%s`\n", file, line, cond); std::abort(); } #endif } // namespace detail } // namespace MDSPAN_IMPL_STANDARD_NAMESPACE #ifndef MDSPAN_IMPL_PRECONDITION_VIOLATION_HANDLER #define MDSPAN_IMPL_PRECONDITION_VIOLATION_HANDLER(cond, file, line) \ MDSPAN_IMPL_STANDARD_NAMESPACE::detail::default_precondition_violation_handler(cond, file, line) #endif #ifndef MDSPAN_IMPL_CHECK_PRECONDITION #ifndef NDEBUG #define MDSPAN_IMPL_CHECK_PRECONDITION 0 #else #define MDSPAN_IMPL_CHECK_PRECONDITION 1 #endif #endif namespace MDSPAN_IMPL_STANDARD_NAMESPACE { namespace detail { template MDSPAN_FUNCTION constexpr void precondition(const char* cond, const char* file, unsigned line) { if (!check) { return; } // in case the macro doesn't use the arguments for custom macros (void) cond; (void) file; (void) line; MDSPAN_IMPL_PRECONDITION_VIOLATION_HANDLER(cond, file, line); } } // namespace detail } // namespace MDSPAN_IMPL_STANDARD_NAMESPACE #define MDSPAN_IMPL_PRECONDITION(...) \ do { \ if (!(__VA_ARGS__)) { \ MDSPAN_IMPL_STANDARD_NAMESPACE::detail::precondition(#__VA_ARGS__, __FILE__, __LINE__); \ } \ } while (0) // end Preprocessor helpers }}}1 //============================================================================== //============================================================================== // {{{1 // These compatibility macros don't help with partial ordering, but they should do the trick // for what we need to do with concepts in mdspan #ifdef _MDSPAN_USE_CONCEPTS # define MDSPAN_CLOSE_ANGLE_REQUIRES(REQ) > requires REQ # define MDSPAN_FUNCTION_REQUIRES(PAREN_PREQUALS, FNAME, PAREN_PARAMS, QUALS, REQ) \ MDSPAN_PP_REMOVE_PARENS(PAREN_PREQUALS) FNAME PAREN_PARAMS QUALS requires REQ \ /**/ #else # define MDSPAN_CLOSE_ANGLE_REQUIRES(REQ) , typename ::std::enable_if<(REQ), int>::type = 0> # define MDSPAN_FUNCTION_REQUIRES(PAREN_PREQUALS, FNAME, PAREN_PARAMS, QUALS, REQ) \ MDSPAN_TEMPLATE_REQUIRES( \ class __function_requires_ignored=void, \ (std::is_void<__function_requires_ignored>::value && REQ) \ ) MDSPAN_PP_REMOVE_PARENS(PAREN_PREQUALS) FNAME PAREN_PARAMS QUALS \ /**/ #endif #if defined(_MDSPAN_COMPILER_MSVC) && (!defined(_MSVC_TRADITIONAL) || _MSVC_TRADITIONAL) # define MDSPAN_TEMPLATE_REQUIRES(...) \ MDSPAN_PP_CAT( \ MDSPAN_PP_CAT(MDSPAN_TEMPLATE_REQUIRES_, MDSPAN_PP_COUNT(__VA_ARGS__))\ (__VA_ARGS__), \ ) \ /**/ #else # define MDSPAN_TEMPLATE_REQUIRES(...) \ MDSPAN_PP_EVAL( \ MDSPAN_PP_CAT(MDSPAN_TEMPLATE_REQUIRES_, MDSPAN_PP_COUNT(__VA_ARGS__)), \ __VA_ARGS__ \ ) \ /**/ #endif #define MDSPAN_TEMPLATE_REQUIRES_2(TP1, REQ) \ template end Concept emulation }}}1 //============================================================================== //============================================================================== // {{{1 #ifdef _MDSPAN_USE_INLINE_VARIABLES # define _MDSPAN_INLINE_VARIABLE inline #else # define _MDSPAN_INLINE_VARIABLE #endif // end inline variables }}}1 //============================================================================== //============================================================================== // {{{1 #if _MDSPAN_USE_RETURN_TYPE_DEDUCTION # define _MDSPAN_DEDUCE_RETURN_TYPE_SINGLE_LINE(SIGNATURE, BODY) \ auto MDSPAN_PP_REMOVE_PARENS(SIGNATURE) { return MDSPAN_PP_REMOVE_PARENS(BODY); } # define _MDSPAN_DEDUCE_DECLTYPE_AUTO_RETURN_TYPE_SINGLE_LINE(SIGNATURE, BODY) \ decltype(auto) MDSPAN_PP_REMOVE_PARENS(SIGNATURE) { return MDSPAN_PP_REMOVE_PARENS(BODY); } #else # define _MDSPAN_DEDUCE_RETURN_TYPE_SINGLE_LINE(SIGNATURE, BODY) \ auto MDSPAN_PP_REMOVE_PARENS(SIGNATURE) \ -> std::remove_cv_t> \ { return MDSPAN_PP_REMOVE_PARENS(BODY); } # define _MDSPAN_DEDUCE_DECLTYPE_AUTO_RETURN_TYPE_SINGLE_LINE(SIGNATURE, BODY) \ auto MDSPAN_PP_REMOVE_PARENS(SIGNATURE) \ -> decltype(BODY) \ { return MDSPAN_PP_REMOVE_PARENS(BODY); } #endif // end Return type deduction }}}1 //============================================================================== //============================================================================== // {{{1 struct __mdspan_enable_fold_comma { }; #ifdef _MDSPAN_USE_FOLD_EXPRESSIONS # define _MDSPAN_FOLD_AND(...) ((__VA_ARGS__) && ...) # define _MDSPAN_FOLD_AND_TEMPLATE(...) ((__VA_ARGS__) && ...) # define _MDSPAN_FOLD_OR(...) ((__VA_ARGS__) || ...) # define _MDSPAN_FOLD_ASSIGN_LEFT(INIT, ...) (INIT = ... = (__VA_ARGS__)) # define _MDSPAN_FOLD_ASSIGN_RIGHT(PACK, ...) (PACK = ... = (__VA_ARGS__)) # define _MDSPAN_FOLD_TIMES_RIGHT(PACK, ...) (PACK * ... * (__VA_ARGS__)) # define _MDSPAN_FOLD_PLUS_RIGHT(PACK, ...) (PACK + ... + (__VA_ARGS__)) # define _MDSPAN_FOLD_COMMA(...) ((__VA_ARGS__), ...) #else namespace MDSPAN_IMPL_STANDARD_NAMESPACE { namespace __fold_compatibility_impl { // We could probably be more clever here, but at the (small) risk of losing some compiler understanding. For the // few operations we need, it's not worth generalizing over the operation #if _MDSPAN_USE_RETURN_TYPE_DEDUCTION MDSPAN_FORCE_INLINE_FUNCTION constexpr decltype(auto) __fold_right_and_impl() { return true; } template MDSPAN_FORCE_INLINE_FUNCTION constexpr decltype(auto) __fold_right_and_impl(Arg&& arg, Args&&... args) { return ((Arg&&)arg) && __fold_compatibility_impl::__fold_right_and_impl((Args&&)args...); } MDSPAN_FORCE_INLINE_FUNCTION constexpr decltype(auto) __fold_right_or_impl() { return false; } template MDSPAN_FORCE_INLINE_FUNCTION constexpr auto __fold_right_or_impl(Arg&& arg, Args&&... args) { return ((Arg&&)arg) || __fold_compatibility_impl::__fold_right_or_impl((Args&&)args...); } template MDSPAN_FORCE_INLINE_FUNCTION constexpr auto __fold_left_assign_impl(Arg1&& arg1) { return (Arg1&&)arg1; } template MDSPAN_FORCE_INLINE_FUNCTION constexpr auto __fold_left_assign_impl(Arg1&& arg1, Arg2&& arg2, Args&&... args) { return __fold_compatibility_impl::__fold_left_assign_impl((((Arg1&&)arg1) = ((Arg2&&)arg2)), (Args&&)args...); } template MDSPAN_FORCE_INLINE_FUNCTION constexpr auto __fold_right_assign_impl(Arg1&& arg1) { return (Arg1&&)arg1; } template MDSPAN_FORCE_INLINE_FUNCTION constexpr auto __fold_right_assign_impl(Arg1&& arg1, Arg2&& arg2, Args&&... args) { return ((Arg1&&)arg1) = __fold_compatibility_impl::__fold_right_assign_impl((Arg2&&)arg2, (Args&&)args...); } template MDSPAN_FORCE_INLINE_FUNCTION constexpr auto __fold_right_plus_impl(Arg1&& arg1) { return (Arg1&&)arg1; } template MDSPAN_FORCE_INLINE_FUNCTION constexpr auto __fold_right_plus_impl(Arg1&& arg1, Arg2&& arg2, Args&&... args) { return ((Arg1&&)arg1) + __fold_compatibility_impl::__fold_right_plus_impl((Arg2&&)arg2, (Args&&)args...); } template MDSPAN_FORCE_INLINE_FUNCTION constexpr auto __fold_right_times_impl(Arg1&& arg1) { return (Arg1&&)arg1; } template MDSPAN_FORCE_INLINE_FUNCTION constexpr auto __fold_right_times_impl(Arg1&& arg1, Arg2&& arg2, Args&&... args) { return ((Arg1&&)arg1) * __fold_compatibility_impl::__fold_right_times_impl((Arg2&&)arg2, (Args&&)args...); } #else //------------------------------------------------------------------------------ // {{{2 template struct __fold_right_and_impl_; template <> struct __fold_right_and_impl_<> { using __rv = bool; MDSPAN_FORCE_INLINE_FUNCTION static constexpr __rv __impl() noexcept { return true; } }; template struct __fold_right_and_impl_ { using __next_t = __fold_right_and_impl_; using __rv = decltype(std::declval() && std::declval()); MDSPAN_FORCE_INLINE_FUNCTION static constexpr __rv __impl(Arg&& arg, Args&&... args) noexcept { return ((Arg&&)arg) && __next_t::__impl((Args&&)args...); } }; template MDSPAN_FORCE_INLINE_FUNCTION constexpr typename __fold_right_and_impl_::__rv __fold_right_and_impl(Args&&... args) { return __fold_right_and_impl_::__impl((Args&&)args...); } // end right and }}}2 //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // {{{2 template struct __fold_right_or_impl_; template <> struct __fold_right_or_impl_<> { using __rv = bool; MDSPAN_FORCE_INLINE_FUNCTION static constexpr __rv __impl() noexcept { return false; } }; template struct __fold_right_or_impl_ { using __next_t = __fold_right_or_impl_; using __rv = decltype(std::declval() || std::declval()); MDSPAN_FORCE_INLINE_FUNCTION static constexpr __rv __impl(Arg&& arg, Args&&... args) noexcept { return ((Arg&&)arg) || __next_t::__impl((Args&&)args...); } }; template MDSPAN_FORCE_INLINE_FUNCTION constexpr typename __fold_right_or_impl_::__rv __fold_right_or_impl(Args&&... args) { return __fold_right_or_impl_::__impl((Args&&)args...); } // end right or }}}2 //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // {{{2 template struct __fold_right_plus_impl_; template struct __fold_right_plus_impl_ { using __rv = Arg&&; MDSPAN_FORCE_INLINE_FUNCTION static constexpr __rv __impl(Arg&& arg) noexcept { return (Arg&&)arg; } }; template struct __fold_right_plus_impl_ { using __next_t = __fold_right_plus_impl_; using __rv = decltype(std::declval() + std::declval()); MDSPAN_FORCE_INLINE_FUNCTION static constexpr __rv __impl(Arg1&& arg, Arg2&& arg2, Args&&... args) noexcept { return ((Arg1&&)arg) + __next_t::__impl((Arg2&&)arg2, (Args&&)args...); } }; template MDSPAN_FORCE_INLINE_FUNCTION constexpr typename __fold_right_plus_impl_::__rv __fold_right_plus_impl(Args&&... args) { return __fold_right_plus_impl_::__impl((Args&&)args...); } // end right plus }}}2 //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // {{{2 template struct __fold_right_times_impl_; template struct __fold_right_times_impl_ { using __rv = Arg&&; MDSPAN_FORCE_INLINE_FUNCTION static constexpr __rv __impl(Arg&& arg) noexcept { return (Arg&&)arg; } }; template struct __fold_right_times_impl_ { using __next_t = __fold_right_times_impl_; using __rv = decltype(std::declval() * std::declval()); MDSPAN_FORCE_INLINE_FUNCTION static constexpr __rv __impl(Arg1&& arg, Arg2&& arg2, Args&&... args) noexcept { return ((Arg1&&)arg) * __next_t::__impl((Arg2&&)arg2, (Args&&)args...); } }; template MDSPAN_FORCE_INLINE_FUNCTION constexpr typename __fold_right_times_impl_::__rv __fold_right_times_impl(Args&&... args) { return __fold_right_times_impl_::__impl((Args&&)args...); } // end right times }}}2 //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // {{{2 template struct __fold_right_assign_impl_; template struct __fold_right_assign_impl_ { using __rv = Arg&&; MDSPAN_FORCE_INLINE_FUNCTION static constexpr __rv __impl(Arg&& arg) noexcept { return (Arg&&)arg; } }; template struct __fold_right_assign_impl_ { using __next_t = __fold_right_assign_impl_; using __rv = decltype(std::declval() = std::declval()); MDSPAN_FORCE_INLINE_FUNCTION static constexpr __rv __impl(Arg1&& arg, Arg2&& arg2, Args&&... args) noexcept { return ((Arg1&&)arg) = __next_t::__impl((Arg2&&)arg2, (Args&&)args...); } }; template MDSPAN_FORCE_INLINE_FUNCTION constexpr typename __fold_right_assign_impl_::__rv __fold_right_assign_impl(Args&&... args) { return __fold_right_assign_impl_::__impl((Args&&)args...); } // end right assign }}}2 //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // {{{2 template struct __fold_left_assign_impl_; template struct __fold_left_assign_impl_ { using __rv = Arg&&; MDSPAN_FORCE_INLINE_FUNCTION static constexpr __rv __impl(Arg&& arg) noexcept { return (Arg&&)arg; } }; template struct __fold_left_assign_impl_ { using __assign_result_t = decltype(std::declval() = std::declval()); using __next_t = __fold_left_assign_impl_<__assign_result_t, Args...>; using __rv = typename __next_t::__rv; MDSPAN_FORCE_INLINE_FUNCTION static constexpr __rv __impl(Arg1&& arg, Arg2&& arg2, Args&&... args) noexcept { return __next_t::__impl(((Arg1&&)arg) = (Arg2&&)arg2, (Args&&)args...); } }; template MDSPAN_FORCE_INLINE_FUNCTION constexpr typename __fold_left_assign_impl_::__rv __fold_left_assign_impl(Args&&... args) { return __fold_left_assign_impl_::__impl((Args&&)args...); } // end left assign }}}2 //------------------------------------------------------------------------------ #endif template constexpr __mdspan_enable_fold_comma __fold_comma_impl(Args&&...) noexcept { return { }; } template struct __bools; } // __fold_compatibility_impl } // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE # define _MDSPAN_FOLD_AND(...) MDSPAN_IMPL_STANDARD_NAMESPACE::__fold_compatibility_impl::__fold_right_and_impl((__VA_ARGS__)...) # define _MDSPAN_FOLD_OR(...) MDSPAN_IMPL_STANDARD_NAMESPACE::__fold_compatibility_impl::__fold_right_or_impl((__VA_ARGS__)...) # define _MDSPAN_FOLD_ASSIGN_LEFT(INIT, ...) MDSPAN_IMPL_STANDARD_NAMESPACE::__fold_compatibility_impl::__fold_left_assign_impl(INIT, (__VA_ARGS__)...) # define _MDSPAN_FOLD_ASSIGN_RIGHT(PACK, ...) MDSPAN_IMPL_STANDARD_NAMESPACE::__fold_compatibility_impl::__fold_right_assign_impl((PACK)..., __VA_ARGS__) # define _MDSPAN_FOLD_TIMES_RIGHT(PACK, ...) MDSPAN_IMPL_STANDARD_NAMESPACE::__fold_compatibility_impl::__fold_right_times_impl((PACK)..., __VA_ARGS__) # define _MDSPAN_FOLD_PLUS_RIGHT(PACK, ...) MDSPAN_IMPL_STANDARD_NAMESPACE::__fold_compatibility_impl::__fold_right_plus_impl((PACK)..., __VA_ARGS__) # define _MDSPAN_FOLD_COMMA(...) MDSPAN_IMPL_STANDARD_NAMESPACE::__fold_compatibility_impl::__fold_comma_impl((__VA_ARGS__)...) # define _MDSPAN_FOLD_AND_TEMPLATE(...) \ _MDSPAN_TRAIT(std::is_same, __fold_compatibility_impl::__bools<(__VA_ARGS__)..., true>, __fold_compatibility_impl::__bools) #endif // end fold expressions }}}1 //============================================================================== //============================================================================== // {{{1 #if _MDSPAN_USE_VARIABLE_TEMPLATES # define _MDSPAN_TRAIT(TRAIT, ...) TRAIT##_v<__VA_ARGS__> #else # define _MDSPAN_TRAIT(TRAIT, ...) TRAIT<__VA_ARGS__>::value #endif // end Variable template compatibility }}}1 //============================================================================== //============================================================================== // {{{1 #if _MDSPAN_USE_CONSTEXPR_14 # define _MDSPAN_CONSTEXPR_14 constexpr // Workaround for a bug (I think?) in EDG frontends # ifdef __EDG__ # define _MDSPAN_CONSTEXPR_14_DEFAULTED # else # define _MDSPAN_CONSTEXPR_14_DEFAULTED constexpr # endif #else # define _MDSPAN_CONSTEXPR_14 # define _MDSPAN_CONSTEXPR_14_DEFAULTED #endif // end Pre-C++14 constexpr }}}1 //============================================================================== //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/macros.hpp #include // size_t namespace MDSPAN_IMPL_STANDARD_NAMESPACE { template struct default_accessor { using offset_policy = default_accessor; using element_type = ElementType; using reference = ElementType&; using data_handle_type = ElementType*; MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr default_accessor() noexcept = default; MDSPAN_TEMPLATE_REQUIRES( class OtherElementType, /* requires */ ( _MDSPAN_TRAIT(std::is_convertible, OtherElementType(*)[], element_type(*)[]) ) ) MDSPAN_INLINE_FUNCTION constexpr default_accessor(default_accessor) noexcept {} MDSPAN_INLINE_FUNCTION constexpr data_handle_type offset(data_handle_type p, size_t i) const noexcept { return p + i; } MDSPAN_FORCE_INLINE_FUNCTION constexpr reference access(data_handle_type p, size_t i) const noexcept { return p[i]; } }; } // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/default_accessor.hpp //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/full_extent_t.hpp //@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // See https://kokkos.org/LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER namespace MDSPAN_IMPL_STANDARD_NAMESPACE { struct full_extent_t { explicit full_extent_t() = default; }; _MDSPAN_INLINE_VARIABLE constexpr auto full_extent = full_extent_t{ }; } // namespace MDSPAN_IMPL_STANDARD_NAMESPACE //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/full_extent_t.hpp //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/mdspan.hpp //@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // See https://kokkos.org/LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/layout_right.hpp //@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // See https://kokkos.org/LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/trait_backports.hpp //@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // See https://kokkos.org/LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER #ifndef MDSPAN_INCLUDE_EXPERIMENTAL_BITS_TRAIT_BACKPORTS_HPP_ #define MDSPAN_INCLUDE_EXPERIMENTAL_BITS_TRAIT_BACKPORTS_HPP_ #include #include // integer_sequence //============================================================================== // {{{1 #ifdef _MDSPAN_NEEDS_TRAIT_VARIABLE_TEMPLATE_BACKPORTS #if _MDSPAN_USE_VARIABLE_TEMPLATES namespace MDSPAN_IMPL_STANDARD_NAMESPACE { #define _MDSPAN_BACKPORT_TRAIT(TRAIT) \ template _MDSPAN_INLINE_VARIABLE constexpr auto TRAIT##_v = TRAIT::value; _MDSPAN_BACKPORT_TRAIT(is_assignable) _MDSPAN_BACKPORT_TRAIT(is_constructible) _MDSPAN_BACKPORT_TRAIT(is_convertible) _MDSPAN_BACKPORT_TRAIT(is_default_constructible) _MDSPAN_BACKPORT_TRAIT(is_trivially_destructible) _MDSPAN_BACKPORT_TRAIT(is_same) _MDSPAN_BACKPORT_TRAIT(is_empty) _MDSPAN_BACKPORT_TRAIT(is_void) #undef _MDSPAN_BACKPORT_TRAIT } // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE #endif // _MDSPAN_USE_VARIABLE_TEMPLATES #endif // _MDSPAN_NEEDS_TRAIT_VARIABLE_TEMPLATE_BACKPORTS // end Variable template trait backports (e.g., is_void_v) }}}1 //============================================================================== //============================================================================== // {{{1 #if !defined(_MDSPAN_USE_INTEGER_SEQUENCE) || !_MDSPAN_USE_INTEGER_SEQUENCE namespace MDSPAN_IMPL_STANDARD_NAMESPACE { template struct integer_sequence { static constexpr size_t size() noexcept { return sizeof...(Vals); } using value_type = T; }; template using index_sequence = std::integer_sequence; namespace __detail { template struct __make_int_seq_impl; template struct __make_int_seq_impl> { using type = integer_sequence; }; template struct __make_int_seq_impl< T, N, I, integer_sequence > : __make_int_seq_impl> { }; } // end namespace __detail template using make_integer_sequence = typename __detail::__make_int_seq_impl>::type; template using make_index_sequence = typename __detail::__make_int_seq_impl>::type; template using index_sequence_for = make_index_sequence; } // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE #endif // end integer sequence (ugh...) }}}1 //============================================================================== //============================================================================== // {{{1 #if !defined(_MDSPAN_USE_STANDARD_TRAIT_ALIASES) || !_MDSPAN_USE_STANDARD_TRAIT_ALIASES namespace MDSPAN_IMPL_STANDARD_NAMESPACE { #define _MDSPAN_BACKPORT_TRAIT_ALIAS(TRAIT) \ template using TRAIT##_t = typename TRAIT::type; _MDSPAN_BACKPORT_TRAIT_ALIAS(remove_cv) _MDSPAN_BACKPORT_TRAIT_ALIAS(remove_reference) template using enable_if_t = typename enable_if<_B, _T>::type; #undef _MDSPAN_BACKPORT_TRAIT_ALIAS } // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE #endif // end standard trait aliases }}}1 //============================================================================== #endif //MDSPAN_INCLUDE_EXPERIMENTAL_BITS_TRAIT_BACKPORTS_HPP_ //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/trait_backports.hpp //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/extents.hpp //@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // See https://kokkos.org/LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/dynamic_extent.hpp //@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // See https://kokkos.org/LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER #if defined(__cpp_lib_span) #include #endif #include // size_t #include // numeric_limits namespace MDSPAN_IMPL_STANDARD_NAMESPACE { #if defined(__cpp_lib_span) using std::dynamic_extent; #else _MDSPAN_INLINE_VARIABLE constexpr auto dynamic_extent = std::numeric_limits::max(); #endif } // namespace MDSPAN_IMPL_STANDARD_NAMESPACE //============================================================================================================== //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/dynamic_extent.hpp //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/utility.hpp #include #include namespace MDSPAN_IMPL_STANDARD_NAMESPACE { namespace detail { // type alias used for rank-based tag dispatch // // this is used to enable alternatives to constexpr if when building for C++14 // template using with_rank = std::integral_constant; template MDSPAN_INLINE_FUNCTION constexpr bool common_integral_compare(I1 x, I2 y) { static_assert(std::is_integral::value && std::is_integral::value, ""); using I = std::common_type_t; return static_cast(x) == static_cast(y); } template MDSPAN_INLINE_FUNCTION constexpr bool rankwise_equal(with_rank<0>, const T1&, const T2&, F) { return true; } template MDSPAN_INLINE_FUNCTION constexpr bool rankwise_equal(with_rank, const T1& x, const T2& y, F func) { bool match = true; for (std::size_t r = 0; r < N; r++) { match = match && common_integral_compare(func(x, r), func(y, r)); } return match; } constexpr struct { template MDSPAN_INLINE_FUNCTION constexpr auto operator()(const T& x, I i) const { return x.extent(i); } } extent; constexpr struct { template MDSPAN_INLINE_FUNCTION constexpr auto operator()(const T& x, I i) const { return x.stride(i); } } stride; } // namespace detail constexpr struct mdspan_non_standard_tag { } mdspan_non_standard; } // namespace MDSPAN_IMPL_STANDARD_NAMESPACE //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/utility.hpp #ifdef __cpp_lib_span #include #endif #include #include #include #include namespace MDSPAN_IMPL_STANDARD_NAMESPACE { namespace detail { // Function used to check compatibility of extents in converting constructor // can't be a private member function for some reason. template MDSPAN_INLINE_FUNCTION static constexpr std::integral_constant __check_compatible_extents( std::integral_constant, std::integer_sequence, std::integer_sequence) noexcept { return {}; } // This helper prevents ICE's on MSVC. template struct __compare_extent_compatible : std::integral_constant {}; template MDSPAN_INLINE_FUNCTION static constexpr std::integral_constant< bool, _MDSPAN_FOLD_AND(__compare_extent_compatible::value)> __check_compatible_extents( std::integral_constant, std::integer_sequence, std::integer_sequence) noexcept { return {}; } template MDSPAN_INLINE_FUNCTION static constexpr bool are_valid_indices() { return _MDSPAN_FOLD_AND(std::is_convertible::value) && _MDSPAN_FOLD_AND(std::is_nothrow_constructible::value); } // ------------------------------------------------------------------ // ------------ static_array ---------------------------------------- // ------------------------------------------------------------------ // array like class which provides an array of static values with get // function and operator []. // Implementation of Static Array with recursive implementation of get. template struct static_array_impl; template struct static_array_impl { MDSPAN_INLINE_FUNCTION constexpr static T get(size_t r) { if (r == R) return FirstExt; else return static_array_impl::get(r); } template MDSPAN_INLINE_FUNCTION constexpr static T get() { #if MDSPAN_HAS_CXX_17 if constexpr (r == R) return FirstExt; else return static_array_impl::template get(); #else get(r); #endif } }; // End the recursion template struct static_array_impl { MDSPAN_INLINE_FUNCTION constexpr static T get(size_t) { return FirstExt; } template MDSPAN_INLINE_FUNCTION constexpr static T get() { return FirstExt; } }; // Don't start recursion if size 0 template struct static_array_impl<0, T> { MDSPAN_INLINE_FUNCTION constexpr static T get(size_t) { return T(); } template MDSPAN_INLINE_FUNCTION constexpr static T get() { return T(); } }; // Static array, provides get(), get(r) and operator[r] template struct static_array: public static_array_impl<0, T, Values...> { public: using value_type = T; MDSPAN_INLINE_FUNCTION constexpr static size_t size() { return sizeof...(Values); } }; // ------------------------------------------------------------------ // ------------ index_sequence_scan --------------------------------- // ------------------------------------------------------------------ // index_sequence_scan takes compile time values and provides get(r) // and get() which return the sum of the first r-1 values. // Recursive implementation for get template struct index_sequence_scan_impl; template struct index_sequence_scan_impl { MDSPAN_INLINE_FUNCTION constexpr static size_t get(size_t r) { if (r > R) return FirstVal + index_sequence_scan_impl::get(r); else return 0; } }; template struct index_sequence_scan_impl { #if defined(__NVCC__) || defined(__NVCOMPILER) || \ defined(_MDSPAN_COMPILER_INTEL) // NVCC warns about pointless comparison with 0 for R==0 and r being const // evaluatable and also 0. MDSPAN_INLINE_FUNCTION constexpr static size_t get(size_t r) { return static_cast(R) > static_cast(r) ? FirstVal : 0; } #else MDSPAN_INLINE_FUNCTION constexpr static size_t get(size_t r) { return R > r ? FirstVal : 0; } #endif }; template <> struct index_sequence_scan_impl<0> { MDSPAN_INLINE_FUNCTION constexpr static size_t get(size_t) { return 0; } }; // ------------------------------------------------------------------ // ------------ possibly_empty_array ------------------------------- // ------------------------------------------------------------------ // array like class which provides get function and operator [], and // has a specialization for the size 0 case. // This is needed to make the maybe_static_array be truly empty, for // all static values. template struct possibly_empty_array { T vals[N]{}; MDSPAN_INLINE_FUNCTION constexpr T &operator[](size_t r) { return vals[r]; } MDSPAN_INLINE_FUNCTION constexpr const T &operator[](size_t r) const { return vals[r]; } }; template struct possibly_empty_array { MDSPAN_INLINE_FUNCTION constexpr T operator[](size_t) { return T(); } MDSPAN_INLINE_FUNCTION constexpr const T operator[](size_t) const { return T(); } }; // ------------------------------------------------------------------ // ------------ maybe_static_array ---------------------------------- // ------------------------------------------------------------------ // array like class which has a mix of static and runtime values but // only stores the runtime values. // The type of the static and the runtime values can be different. // The position of a dynamic value is indicated through a tag value. template struct maybe_static_array { static_assert(std::is_convertible::value, "maybe_static_array: TStatic must be convertible to TDynamic"); static_assert(std::is_convertible::value, "maybe_static_array: TDynamic must be convertible to TStatic"); private: // Static values member using static_vals_t = static_array; constexpr static size_t m_size = sizeof...(Values); constexpr static size_t m_size_dynamic = _MDSPAN_FOLD_PLUS_RIGHT((Values == dyn_tag), 0); // Dynamic values member _MDSPAN_NO_UNIQUE_ADDRESS possibly_empty_array m_dyn_vals; // static mapping of indices to the position in the dynamic values array using dyn_map_t = index_sequence_scan_impl<0, static_cast(Values == dyn_tag)...>; public: // two types for static and dynamic values using value_type = TDynamic; using static_value_type = TStatic; // tag value indicating dynamic value constexpr static static_value_type tag_value = dyn_tag; constexpr maybe_static_array() = default; // constructor for all static values // TODO: add precondition check? MDSPAN_TEMPLATE_REQUIRES(class... Vals, /* requires */ ((m_size_dynamic == 0) && (sizeof...(Vals) > 0))) MDSPAN_INLINE_FUNCTION constexpr maybe_static_array(Vals...) : m_dyn_vals{} {} // constructors from dynamic values only MDSPAN_TEMPLATE_REQUIRES(class... DynVals, /* requires */ (sizeof...(DynVals) == m_size_dynamic && m_size_dynamic > 0)) MDSPAN_INLINE_FUNCTION constexpr maybe_static_array(DynVals... vals) : m_dyn_vals{static_cast(vals)...} {} MDSPAN_TEMPLATE_REQUIRES(class T, size_t N, /* requires */ (N == m_size_dynamic && N > 0)) MDSPAN_INLINE_FUNCTION constexpr maybe_static_array(const std::array &vals) { for (size_t r = 0; r < N; r++) m_dyn_vals[r] = static_cast(vals[r]); } MDSPAN_TEMPLATE_REQUIRES(class T, size_t N, /* requires */ (N == m_size_dynamic && N == 0)) MDSPAN_INLINE_FUNCTION constexpr maybe_static_array(const std::array &) : m_dyn_vals{} {} #ifdef __cpp_lib_span MDSPAN_TEMPLATE_REQUIRES(class T, size_t N, /* requires */ (N == m_size_dynamic && N > 0)) MDSPAN_INLINE_FUNCTION constexpr maybe_static_array(const std::span &vals) { for (size_t r = 0; r < N; r++) m_dyn_vals[r] = static_cast(vals[r]); } MDSPAN_TEMPLATE_REQUIRES(class T, size_t N, /* requires */ (N == m_size_dynamic && N == 0)) MDSPAN_INLINE_FUNCTION constexpr maybe_static_array(const std::span &) : m_dyn_vals{} {} #endif // constructors from all values MDSPAN_TEMPLATE_REQUIRES(class... DynVals, /* requires */ (sizeof...(DynVals) != m_size_dynamic && m_size_dynamic > 0)) MDSPAN_INLINE_FUNCTION constexpr maybe_static_array(DynVals... vals) : m_dyn_vals{} { static_assert((sizeof...(DynVals) == m_size), "Invalid number of values."); TDynamic values[m_size]{static_cast(vals)...}; for (size_t r = 0; r < m_size; r++) { TStatic static_val = static_vals_t::get(r); if (static_val == dyn_tag) { m_dyn_vals[dyn_map_t::get(r)] = values[r]; } // Precondition check #ifdef _MDSPAN_DEBUG else { assert(values[r] == static_cast(static_val)); } #endif } } MDSPAN_TEMPLATE_REQUIRES( class T, size_t N, /* requires */ (N != m_size_dynamic && m_size_dynamic > 0)) MDSPAN_INLINE_FUNCTION constexpr maybe_static_array(const std::array &vals) { static_assert((N == m_size), "Invalid number of values."); // Precondition check #ifdef _MDSPAN_DEBUG assert(N == m_size); #endif for (size_t r = 0; r < m_size; r++) { TStatic static_val = static_vals_t::get(r); if (static_val == dyn_tag) { m_dyn_vals[dyn_map_t::get(r)] = static_cast(vals[r]); } // Precondition check #ifdef _MDSPAN_DEBUG else { assert(static_cast(vals[r]) == static_cast(static_val)); } #endif } } #ifdef __cpp_lib_span MDSPAN_TEMPLATE_REQUIRES( class T, size_t N, /* requires */ (N != m_size_dynamic && m_size_dynamic > 0)) MDSPAN_INLINE_FUNCTION constexpr maybe_static_array(const std::span &vals) { static_assert((N == m_size) || (m_size == dynamic_extent)); #ifdef _MDSPAN_DEBUG assert(N == m_size); #endif for (size_t r = 0; r < m_size; r++) { TStatic static_val = static_vals_t::get(r); if (static_val == dyn_tag) { m_dyn_vals[dyn_map_t::get(r)] = static_cast(vals[r]); } #ifdef _MDSPAN_DEBUG else { assert(static_cast(vals[r]) == static_cast(static_val)); } #endif } } #endif // access functions MDSPAN_INLINE_FUNCTION constexpr static TStatic static_value(size_t r) { return static_vals_t::get(r); } MDSPAN_INLINE_FUNCTION constexpr TDynamic value(size_t r) const { TStatic static_val = static_vals_t::get(r); return static_val == dyn_tag ? m_dyn_vals[dyn_map_t::get(r)] : static_cast(static_val); } MDSPAN_INLINE_FUNCTION constexpr TDynamic operator[](size_t r) const { return value(r); } // observers MDSPAN_INLINE_FUNCTION constexpr static size_t size() { return m_size; } MDSPAN_INLINE_FUNCTION constexpr static size_t size_dynamic() { return m_size_dynamic; } }; } // namespace detail } // namespace MDSPAN_IMPL_STANDARD_NAMESPACE namespace MDSPAN_IMPL_STANDARD_NAMESPACE { // ------------------------------------------------------------------ // ------------ extents --------------------------------------------- // ------------------------------------------------------------------ // Class to describe the extents of a multi dimensional array. // Used by mdspan, mdarray and layout mappings. // See ISO C++ standard [mdspan.extents] template class extents { public: // typedefs for integral types used using index_type = IndexType; using size_type = std::make_unsigned_t; using rank_type = size_t; static_assert(std::is_integral::value && !std::is_same::value, MDSPAN_IMPL_STANDARD_NAMESPACE_STRING "::extents::index_type must be a signed or unsigned integer type"); private: constexpr static rank_type m_rank = sizeof...(Extents); constexpr static rank_type m_rank_dynamic = _MDSPAN_FOLD_PLUS_RIGHT((Extents == dynamic_extent), /* + ... + */ 0); // internal storage type using maybe_static_array using vals_t = detail::maybe_static_array; _MDSPAN_NO_UNIQUE_ADDRESS vals_t m_vals; public: // [mdspan.extents.obs], observers of multidimensional index space MDSPAN_INLINE_FUNCTION constexpr static rank_type rank() noexcept { return m_rank; } MDSPAN_INLINE_FUNCTION constexpr static rank_type rank_dynamic() noexcept { return m_rank_dynamic; } MDSPAN_INLINE_FUNCTION constexpr index_type extent(rank_type r) const noexcept { return m_vals.value(r); } MDSPAN_INLINE_FUNCTION constexpr static size_t static_extent(rank_type r) noexcept { return vals_t::static_value(r); } // [mdspan.extents.cons], constructors MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr extents() noexcept = default; // Construction from just dynamic or all values. // Precondition check is deferred to maybe_static_array constructor MDSPAN_TEMPLATE_REQUIRES( class... OtherIndexTypes, /* requires */ ( _MDSPAN_FOLD_AND(_MDSPAN_TRAIT(std::is_convertible, OtherIndexTypes, index_type) /* && ... */) && _MDSPAN_FOLD_AND(_MDSPAN_TRAIT(std::is_nothrow_constructible, index_type, OtherIndexTypes) /* && ... */) && (sizeof...(OtherIndexTypes) == m_rank || sizeof...(OtherIndexTypes) == m_rank_dynamic))) MDSPAN_INLINE_FUNCTION constexpr explicit extents(OtherIndexTypes... dynvals) noexcept : m_vals(static_cast(dynvals)...) {} MDSPAN_TEMPLATE_REQUIRES( class OtherIndexType, size_t N, /* requires */ ( _MDSPAN_TRAIT(std::is_convertible, const OtherIndexType&, index_type) && _MDSPAN_TRAIT(std::is_nothrow_constructible, index_type, const OtherIndexType&) && (N == m_rank || N == m_rank_dynamic))) MDSPAN_INLINE_FUNCTION MDSPAN_CONDITIONAL_EXPLICIT(N != m_rank_dynamic) constexpr extents(const std::array &exts) noexcept : m_vals(std::move(exts)) {} #ifdef __cpp_lib_span MDSPAN_TEMPLATE_REQUIRES( class OtherIndexType, size_t N, /* requires */ (_MDSPAN_TRAIT(std::is_convertible, const OtherIndexType&, index_type) && _MDSPAN_TRAIT(std::is_nothrow_constructible, index_type, const OtherIndexType&) && (N == m_rank || N == m_rank_dynamic))) MDSPAN_INLINE_FUNCTION MDSPAN_CONDITIONAL_EXPLICIT(N != m_rank_dynamic) constexpr extents(const std::span &exts) noexcept : m_vals(std::move(exts)) {} #endif private: // Function to construct extents storage from other extents. // With C++ 17 the first two variants could be collapsed using if constexpr // in which case you don't need all the requires clauses. // in C++ 14 mode that doesn't work due to infinite recursion MDSPAN_TEMPLATE_REQUIRES( size_t DynCount, size_t R, class OtherExtents, class... DynamicValues, /* requires */ ((R < m_rank) && (static_extent(R) == dynamic_extent))) MDSPAN_INLINE_FUNCTION constexpr vals_t __construct_vals_from_extents(std::integral_constant, std::integral_constant, const OtherExtents &exts, DynamicValues... dynamic_values) noexcept { return __construct_vals_from_extents( std::integral_constant(), std::integral_constant(), exts, dynamic_values..., exts.extent(R)); } MDSPAN_TEMPLATE_REQUIRES( size_t DynCount, size_t R, class OtherExtents, class... DynamicValues, /* requires */ ((R < m_rank) && (static_extent(R) != dynamic_extent))) MDSPAN_INLINE_FUNCTION constexpr vals_t __construct_vals_from_extents(std::integral_constant, std::integral_constant, const OtherExtents &exts, DynamicValues... dynamic_values) noexcept { return __construct_vals_from_extents( std::integral_constant(), std::integral_constant(), exts, dynamic_values...); } MDSPAN_TEMPLATE_REQUIRES( size_t DynCount, size_t R, class OtherExtents, class... DynamicValues, /* requires */ ((R == m_rank) && (DynCount == m_rank_dynamic))) MDSPAN_INLINE_FUNCTION constexpr vals_t __construct_vals_from_extents(std::integral_constant, std::integral_constant, const OtherExtents &, DynamicValues... dynamic_values) noexcept { return vals_t{static_cast(dynamic_values)...}; } public: // Converting constructor from other extents specializations MDSPAN_TEMPLATE_REQUIRES( class OtherIndexType, size_t... OtherExtents, /* requires */ ( /* multi-stage check to protect from invalid pack expansion when sizes don't match? */ decltype(detail::__check_compatible_extents( // using: sizeof...(Extents) == sizeof...(OtherExtents) as the second argument fails with MSVC+NVCC with some obscure expansion error // MSVC: 19.38.33133 NVCC: 12.0 std::integral_constant::rank() == extents::rank()>{}, std::integer_sequence{}, std::integer_sequence{}))::value ) ) MDSPAN_INLINE_FUNCTION MDSPAN_CONDITIONAL_EXPLICIT((((Extents != dynamic_extent) && (OtherExtents == dynamic_extent)) || ...) || (std::numeric_limits::max() < std::numeric_limits::max())) constexpr extents(const extents &other) noexcept : m_vals(__construct_vals_from_extents( std::integral_constant(), std::integral_constant(), other)) {} // Comparison operator template MDSPAN_INLINE_FUNCTION friend constexpr bool operator==(const extents &lhs, const extents &rhs) noexcept { return rank() == extents::rank() && detail::rankwise_equal(detail::with_rank{}, rhs, lhs, detail::extent); } #if !(MDSPAN_HAS_CXX_20) template MDSPAN_INLINE_FUNCTION friend constexpr bool operator!=(extents const &lhs, extents const &rhs) noexcept { return !(lhs == rhs); } #endif }; // Recursive helper classes to implement dextents alias for extents namespace detail { template > struct __make_dextents; template struct __make_dextents< IndexType, Rank, ::MDSPAN_IMPL_STANDARD_NAMESPACE::extents> { using type = typename __make_dextents< IndexType, Rank - 1, ::MDSPAN_IMPL_STANDARD_NAMESPACE::extents>::type; }; template struct __make_dextents< IndexType, 0, ::MDSPAN_IMPL_STANDARD_NAMESPACE::extents> { using type = ::MDSPAN_IMPL_STANDARD_NAMESPACE::extents; }; } // end namespace detail // [mdspan.extents.dextents], alias template template using dextents = typename detail::__make_dextents::type; // Deduction guide for extents #if defined(_MDSPAN_USE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION) template extents(IndexTypes...) -> extents; #endif // Helper type traits for identifying a class as extents. namespace detail { template struct __is_extents : ::std::false_type {}; template struct __is_extents<::MDSPAN_IMPL_STANDARD_NAMESPACE::extents> : ::std::true_type {}; template #if MDSPAN_HAS_CXX_17 inline #else static #endif constexpr bool __is_extents_v = __is_extents::value; template MDSPAN_INLINE_FUNCTION constexpr void check_lower_bound(InputIndexType user_index, ExtentsIndexType /* current_extent */, std::true_type /* is_signed */) { (void) user_index; // prevent unused variable warning #ifdef _MDSPAN_DEBUG assert(static_cast(user_index) >= 0); #endif } template MDSPAN_INLINE_FUNCTION constexpr void check_lower_bound(InputIndexType /* user_index */, ExtentsIndexType /* current_extent */, std::false_type /* is_signed */) {} template MDSPAN_INLINE_FUNCTION constexpr void check_upper_bound(InputIndexType user_index, ExtentsIndexType current_extent) { (void) user_index; // prevent unused variable warnings (void) current_extent; #ifdef _MDSPAN_DEBUG assert(static_cast(user_index) < current_extent); #endif } // Returning true to use AND fold instead of comma // CPP14 mode doesn't like the use of void expressions // with the way the _MDSPAN_FOLD_AND is set up template MDSPAN_INLINE_FUNCTION constexpr bool check_one_index(InputIndex user_index, ExtentsIndexType current_extent) { check_lower_bound(user_index, current_extent, std::integral_constant::value>{}); check_upper_bound(user_index, current_extent); return true; } template MDSPAN_INLINE_FUNCTION constexpr void check_all_indices_helper(std::index_sequence, const extents& exts, Indices... indices) { // Suppress warning about statement has no effect (void) _MDSPAN_FOLD_AND( (check_one_index(indices, exts.extent(RankIndices))) ); } template MDSPAN_INLINE_FUNCTION constexpr void check_all_indices(const extents& exts, Indices... indices) { check_all_indices_helper(std::make_index_sequence(), exts, indices...); } } // namespace detail } // namespace MDSPAN_IMPL_STANDARD_NAMESPACE //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/extents.hpp //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/layout_stride.hpp //@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // See https://kokkos.org/LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/compressed_pair.hpp //@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // See https://kokkos.org/LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER #if !defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/no_unique_address.hpp //@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // See https://kokkos.org/LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER namespace MDSPAN_IMPL_STANDARD_NAMESPACE { namespace detail { //============================================================================== template struct __no_unique_address_emulation { using __stored_type = _T; _T __v; MDSPAN_FORCE_INLINE_FUNCTION constexpr _T const &__ref() const noexcept { return __v; } MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 _T &__ref() noexcept { return __v; } }; // Empty case // This doesn't work if _T is final, of course, but we're not using anything // like that currently. That kind of thing could be added pretty easily though template struct __no_unique_address_emulation< _T, _Disambiguator, std::enable_if_t<_MDSPAN_TRAIT(std::is_empty, _T) && // If the type isn't trivially destructible, its destructor // won't be called at the right time, so don't use this // specialization _MDSPAN_TRAIT(std::is_trivially_destructible, _T)>> : #ifdef _MDSPAN_COMPILER_MSVC // MSVC doesn't allow you to access public static member functions of a type // when you *happen* to privately inherit from that type. protected #else // But we still want this to be private if possible so that we don't accidentally // access members of _T directly rather than calling __ref() first, which wouldn't // work if _T happens to be stateful and thus we're using the unspecialized definition // of __no_unique_address_emulation above. private #endif _T { using __stored_type = _T; MDSPAN_FORCE_INLINE_FUNCTION constexpr _T const &__ref() const noexcept { return *static_cast<_T const *>(this); } MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 _T &__ref() noexcept { return *static_cast<_T *>(this); } MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr __no_unique_address_emulation() noexcept = default; MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr __no_unique_address_emulation( __no_unique_address_emulation const &) noexcept = default; MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr __no_unique_address_emulation( __no_unique_address_emulation &&) noexcept = default; MDSPAN_INLINE_FUNCTION_DEFAULTED _MDSPAN_CONSTEXPR_14_DEFAULTED __no_unique_address_emulation & operator=(__no_unique_address_emulation const &) noexcept = default; MDSPAN_INLINE_FUNCTION_DEFAULTED _MDSPAN_CONSTEXPR_14_DEFAULTED __no_unique_address_emulation & operator=(__no_unique_address_emulation &&) noexcept = default; MDSPAN_INLINE_FUNCTION_DEFAULTED ~__no_unique_address_emulation() noexcept = default; // Explicitly make this not a reference so that the copy or move // constructor still gets called. MDSPAN_INLINE_FUNCTION explicit constexpr __no_unique_address_emulation(_T const& __v) noexcept : _T(__v) {} MDSPAN_INLINE_FUNCTION explicit constexpr __no_unique_address_emulation(_T&& __v) noexcept : _T(::std::move(__v)) {} }; //============================================================================== } // end namespace detail } // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/no_unique_address.hpp #endif namespace MDSPAN_IMPL_STANDARD_NAMESPACE { namespace detail { // For no unique address emulation, this is the case taken when neither are empty. // For real `[[no_unique_address]]`, this case is always taken. template struct __compressed_pair { _MDSPAN_NO_UNIQUE_ADDRESS _T1 __t1_val{}; _MDSPAN_NO_UNIQUE_ADDRESS _T2 __t2_val{}; MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 _T1 &__first() noexcept { return __t1_val; } MDSPAN_FORCE_INLINE_FUNCTION constexpr _T1 const &__first() const noexcept { return __t1_val; } MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 _T2 &__second() noexcept { return __t2_val; } MDSPAN_FORCE_INLINE_FUNCTION constexpr _T2 const &__second() const noexcept { return __t2_val; } MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr __compressed_pair() = default; MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr __compressed_pair(__compressed_pair const &) = default; MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr __compressed_pair(__compressed_pair &&) = default; MDSPAN_INLINE_FUNCTION_DEFAULTED _MDSPAN_CONSTEXPR_14_DEFAULTED __compressed_pair & operator=(__compressed_pair const &) = default; MDSPAN_INLINE_FUNCTION_DEFAULTED _MDSPAN_CONSTEXPR_14_DEFAULTED __compressed_pair & operator=(__compressed_pair &&) = default; MDSPAN_INLINE_FUNCTION_DEFAULTED ~__compressed_pair() = default; template MDSPAN_INLINE_FUNCTION constexpr __compressed_pair(_T1Like &&__t1, _T2Like &&__t2) : __t1_val((_T1Like &&) __t1), __t2_val((_T2Like &&) __t2) {} }; #if !defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) // First empty. template struct __compressed_pair< _T1, _T2, std::enable_if_t<_MDSPAN_TRAIT(std::is_empty, _T1) && !_MDSPAN_TRAIT(std::is_empty, _T2)>> : private _T1 { _T2 __t2_val{}; MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 _T1 &__first() noexcept { return *static_cast<_T1 *>(this); } MDSPAN_FORCE_INLINE_FUNCTION constexpr _T1 const &__first() const noexcept { return *static_cast<_T1 const *>(this); } MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 _T2 &__second() noexcept { return __t2_val; } MDSPAN_FORCE_INLINE_FUNCTION constexpr _T2 const &__second() const noexcept { return __t2_val; } MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr __compressed_pair() = default; MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr __compressed_pair(__compressed_pair const &) = default; MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr __compressed_pair(__compressed_pair &&) = default; MDSPAN_INLINE_FUNCTION_DEFAULTED _MDSPAN_CONSTEXPR_14_DEFAULTED __compressed_pair & operator=(__compressed_pair const &) = default; MDSPAN_INLINE_FUNCTION_DEFAULTED _MDSPAN_CONSTEXPR_14_DEFAULTED __compressed_pair & operator=(__compressed_pair &&) = default; MDSPAN_INLINE_FUNCTION_DEFAULTED ~__compressed_pair() = default; template MDSPAN_INLINE_FUNCTION constexpr __compressed_pair(_T1Like &&__t1, _T2Like &&__t2) : _T1((_T1Like &&) __t1), __t2_val((_T2Like &&) __t2) {} }; // Second empty. template struct __compressed_pair< _T1, _T2, std::enable_if_t> : private _T2 { _T1 __t1_val{}; MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 _T1 &__first() noexcept { return __t1_val; } MDSPAN_FORCE_INLINE_FUNCTION constexpr _T1 const &__first() const noexcept { return __t1_val; } MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 _T2 &__second() noexcept { return *static_cast<_T2 *>(this); } MDSPAN_FORCE_INLINE_FUNCTION constexpr _T2 const &__second() const noexcept { return *static_cast<_T2 const *>(this); } MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr __compressed_pair() = default; MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr __compressed_pair(__compressed_pair const &) = default; MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr __compressed_pair(__compressed_pair &&) = default; MDSPAN_INLINE_FUNCTION_DEFAULTED _MDSPAN_CONSTEXPR_14_DEFAULTED __compressed_pair & operator=(__compressed_pair const &) = default; MDSPAN_INLINE_FUNCTION_DEFAULTED _MDSPAN_CONSTEXPR_14_DEFAULTED __compressed_pair & operator=(__compressed_pair &&) = default; MDSPAN_INLINE_FUNCTION_DEFAULTED ~__compressed_pair() = default; template MDSPAN_INLINE_FUNCTION constexpr __compressed_pair(_T1Like &&__t1, _T2Like &&__t2) : _T2((_T2Like &&) __t2), __t1_val((_T1Like &&) __t1) {} }; // Both empty. template struct __compressed_pair< _T1, _T2, std::enable_if_t<_MDSPAN_TRAIT(std::is_empty, _T1) && _MDSPAN_TRAIT(std::is_empty, _T2)>> // We need to use the __no_unique_address_emulation wrapper here to avoid // base class ambiguities. #ifdef _MDSPAN_COMPILER_MSVC // MSVC doesn't allow you to access public static member functions of a type // when you *happen* to privately inherit from that type. : protected __no_unique_address_emulation<_T1, 0>, protected __no_unique_address_emulation<_T2, 1> #else : private __no_unique_address_emulation<_T1, 0>, private __no_unique_address_emulation<_T2, 1> #endif { using __first_base_t = __no_unique_address_emulation<_T1, 0>; using __second_base_t = __no_unique_address_emulation<_T2, 1>; MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 _T1 &__first() noexcept { return this->__first_base_t::__ref(); } MDSPAN_FORCE_INLINE_FUNCTION constexpr _T1 const &__first() const noexcept { return this->__first_base_t::__ref(); } MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 _T2 &__second() noexcept { return this->__second_base_t::__ref(); } MDSPAN_FORCE_INLINE_FUNCTION constexpr _T2 const &__second() const noexcept { return this->__second_base_t::__ref(); } MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr __compressed_pair() = default; MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr __compressed_pair(__compressed_pair const &) = default; MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr __compressed_pair(__compressed_pair &&) = default; MDSPAN_INLINE_FUNCTION_DEFAULTED _MDSPAN_CONSTEXPR_14_DEFAULTED __compressed_pair & operator=(__compressed_pair const &) = default; MDSPAN_INLINE_FUNCTION_DEFAULTED _MDSPAN_CONSTEXPR_14_DEFAULTED __compressed_pair & operator=(__compressed_pair &&) = default; MDSPAN_INLINE_FUNCTION_DEFAULTED ~__compressed_pair() = default; template MDSPAN_INLINE_FUNCTION constexpr __compressed_pair(_T1Like &&__t1, _T2Like &&__t2) noexcept : __first_base_t(_T1((_T1Like &&) __t1)), __second_base_t(_T2((_T2Like &&) __t2)) { } }; #endif // !defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) } // end namespace detail } // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/compressed_pair.hpp #if !defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) #endif #include #include #include #ifdef __cpp_lib_span #include #endif #if defined(_MDSPAN_USE_CONCEPTS) && MDSPAN_HAS_CXX_20 && defined(__cpp_lib_concepts) # include #endif namespace MDSPAN_IMPL_STANDARD_NAMESPACE { struct layout_left { template class mapping; }; struct layout_right { template class mapping; }; namespace detail { template constexpr bool __is_mapping_of = std::is_same, Mapping>::value; #if defined(_MDSPAN_USE_CONCEPTS) && MDSPAN_HAS_CXX_20 # if !defined(__cpp_lib_concepts) namespace internal { namespace detail { template concept __same_as = std::is_same_v<_Tp, _Up>; } // namespace detail template concept __same_as = detail::__same_as && detail::__same_as; } // namespace internal # endif template concept __layout_mapping_alike = requires { requires __is_extents::value; #if defined(__cpp_lib_concepts) { M::is_always_strided() } -> std::same_as; { M::is_always_exhaustive() } -> std::same_as; { M::is_always_unique() } -> std::same_as; #else { M::is_always_strided() } -> internal::__same_as; { M::is_always_exhaustive() } -> internal::__same_as; { M::is_always_unique() } -> internal::__same_as; #endif std::bool_constant::value; std::bool_constant::value; std::bool_constant::value; }; #endif } // namespace detail struct layout_stride { template class mapping #if !defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) : private detail::__no_unique_address_emulation< detail::__compressed_pair< Extents, detail::possibly_empty_array > > #endif { public: using extents_type = Extents; using index_type = typename extents_type::index_type; using size_type = typename extents_type::size_type; using rank_type = typename extents_type::rank_type; using layout_type = layout_stride; // This could be a `requires`, but I think it's better and clearer as a `static_assert`. static_assert(detail::__is_extents_v, MDSPAN_IMPL_STANDARD_NAMESPACE_STRING "::layout_stride::mapping must be instantiated with a specialization of " MDSPAN_IMPL_STANDARD_NAMESPACE_STRING "::extents."); private: //---------------------------------------------------------------------------- using __strides_storage_t = detail::possibly_empty_array; using __member_pair_t = detail::__compressed_pair; #if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) _MDSPAN_NO_UNIQUE_ADDRESS __member_pair_t __members; #else using __base_t = detail::__no_unique_address_emulation<__member_pair_t>; #endif MDSPAN_FORCE_INLINE_FUNCTION constexpr __strides_storage_t const& __strides_storage() const noexcept { #if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) return __members.__second(); #else return this->__base_t::__ref().__second(); #endif } MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 __strides_storage_t& __strides_storage() noexcept { #if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) return __members.__second(); #else return this->__base_t::__ref().__second(); #endif } template _MDSPAN_HOST_DEVICE constexpr index_type __get_size(::MDSPAN_IMPL_STANDARD_NAMESPACE::extents,std::integer_sequence) const { return _MDSPAN_FOLD_TIMES_RIGHT( static_cast(extents().extent(Idx)), 1 ); } //---------------------------------------------------------------------------- template friend class mapping; //---------------------------------------------------------------------------- // Workaround for non-deducibility of the index sequence template parameter if it's given at the top level template struct __deduction_workaround; template struct __deduction_workaround> { template MDSPAN_INLINE_FUNCTION static constexpr bool _eq_impl(mapping const& self, mapping const& other) noexcept { using common_t = std::common_type_t; return _MDSPAN_FOLD_AND((static_cast(self.stride(Idxs)) == static_cast(other.stride(Idxs))) /* && ... */) && _MDSPAN_FOLD_AND((static_cast(self.extents().extent(Idxs)) == static_cast(other.extents().extent(Idxs))) /* || ... */); } template MDSPAN_INLINE_FUNCTION static constexpr bool _not_eq_impl(mapping const& self, mapping const& other) noexcept { using common_t = std::common_type_t; return _MDSPAN_FOLD_OR((static_cast(self.stride(Idxs)) != static_cast(other.stride(Idxs))) /* || ... */) || _MDSPAN_FOLD_OR((static_cast(self.extents().extent(Idxs)) != static_cast(other.extents().extent(Idxs))) /* || ... */); } template MDSPAN_FORCE_INLINE_FUNCTION static constexpr size_t _call_op_impl(mapping const& self, Integral... idxs) noexcept { return _MDSPAN_FOLD_PLUS_RIGHT((idxs * self.stride(Idxs)), /* + ... + */ 0); } MDSPAN_INLINE_FUNCTION static constexpr size_t _req_span_size_impl(mapping const& self) noexcept { // assumes no negative strides; not sure if I'm allowed to assume that or not return __impl::_call_op_impl(self, (self.extents().template __extent() - 1)...) + 1; } template MDSPAN_INLINE_FUNCTION static constexpr const __strides_storage_t fill_strides(const OtherMapping& map) { return __strides_storage_t{static_cast(map.stride(Idxs))...}; } MDSPAN_INLINE_FUNCTION static constexpr const __strides_storage_t& fill_strides(const __strides_storage_t& s) { return s; } template MDSPAN_INLINE_FUNCTION static constexpr const __strides_storage_t fill_strides(const std::array& s) { return __strides_storage_t{static_cast(s[Idxs])...}; } MDSPAN_TEMPLATE_REQUIRES( class IntegralType, // The is_convertible condition is added to make sfinae valid // the extents_type::rank() > 0 is added to avoid use of non-standard zero length c-array (std::is_convertible::value && (extents_type::rank() > 0)) ) MDSPAN_INLINE_FUNCTION // despite the requirement some compilers still complain about zero length array during parsing // making it length 1 now, but since the thing can't be instantiated due to requirement the actual // instantiation of strides_storage will not fail despite mismatching length static constexpr const __strides_storage_t fill_strides(mdspan_non_standard_tag, const IntegralType (&s)[extents_type::rank()>0?extents_type::rank():1]) { return __strides_storage_t{static_cast(s[Idxs])...}; } #ifdef __cpp_lib_span template MDSPAN_INLINE_FUNCTION static constexpr const __strides_storage_t fill_strides(const std::span& s) { return __strides_storage_t{static_cast(s[Idxs])...}; } #endif MDSPAN_INLINE_FUNCTION static constexpr std::array return_strides(const __strides_storage_t& s) { return std::array{s[Idxs]...}; } template MDSPAN_INLINE_FUNCTION static constexpr size_t __return_zero() { return 0; } template MDSPAN_INLINE_FUNCTION static constexpr typename Mapping::index_type __OFFSET(const Mapping& m) { return m(__return_zero()...); } }; // Can't use defaulted parameter in the __deduction_workaround template because of a bug in MSVC warning C4348. using __impl = __deduction_workaround>; static constexpr __strides_storage_t strides_storage(detail::with_rank<0>) { return {}; } template static constexpr __strides_storage_t strides_storage(detail::with_rank) { __strides_storage_t s{}; extents_type e; index_type stride = 1; for(int r = static_cast(extents_type::rank() - 1); r >= 0; r--) { s[r] = stride; stride *= e.extent(r); } return s; } //---------------------------------------------------------------------------- #if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) MDSPAN_INLINE_FUNCTION constexpr explicit mapping(__member_pair_t&& __m) : __members(::std::move(__m)) {} #else MDSPAN_INLINE_FUNCTION constexpr explicit mapping(__base_t&& __b) : __base_t(::std::move(__b)) {} #endif public: //-------------------------------------------------------------------------------- MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping() noexcept #if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) : __members{ #else : __base_t(__base_t{__member_pair_t( #endif extents_type(), __strides_storage_t(strides_storage(detail::with_rank{})) #if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) } #else )}) #endif {} MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping(mapping const&) noexcept = default; MDSPAN_TEMPLATE_REQUIRES( class IntegralTypes, /* requires */ ( // MSVC 19.32 does not like using index_type here, requires the typename Extents::index_type // error C2641: cannot deduce template arguments for 'MDSPAN_IMPL_STANDARD_NAMESPACE::layout_stride::mapping' _MDSPAN_TRAIT(std::is_convertible, const std::remove_const_t&, typename Extents::index_type) && _MDSPAN_TRAIT(std::is_nothrow_constructible, typename Extents::index_type, const std::remove_const_t&) ) ) MDSPAN_INLINE_FUNCTION constexpr mapping( extents_type const& e, std::array const& s ) noexcept #if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) : __members{ #else : __base_t(__base_t{__member_pair_t( #endif e, __strides_storage_t(__impl::fill_strides(s)) #if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) } #else )}) #endif { /* * TODO: check preconditions * - s[i] > 0 is true for all i in the range [0, rank_ ). * - REQUIRED-SPAN-SIZE(e, s) is a representable value of type index_type ([basic.fundamental]). * - If rank_ is greater than 0, then there exists a permutation P of the integers in the * range [0, rank_), such that s[ pi ] >= s[ pi − 1 ] * e.extent( pi − 1 ) is true for * all i in the range [1, rank_ ), where pi is the ith element of P. */ } MDSPAN_TEMPLATE_REQUIRES( class IntegralTypes, /* requires */ ( // MSVC 19.32 does not like using index_type here, requires the typename Extents::index_type // error C2641: cannot deduce template arguments for 'MDSPAN_IMPL_STANDARD_NAMESPACE::layout_stride::mapping' _MDSPAN_TRAIT(std::is_convertible, const std::remove_const_t&, typename Extents::index_type) && _MDSPAN_TRAIT(std::is_nothrow_constructible, typename Extents::index_type, const std::remove_const_t&) && (Extents::rank() > 0) ) ) MDSPAN_INLINE_FUNCTION constexpr mapping( mdspan_non_standard_tag, extents_type const& e, // despite the requirement some compilers still complain about zero length array during parsing // making it length 1 now, but since the thing can't be instantiated due to requirement the actual // instantiation of strides_storage will not fail despite mismatching length IntegralTypes (&s)[extents_type::rank()>0?extents_type::rank():1] ) noexcept #if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) : __members{ #else : __base_t(__base_t{__member_pair_t( #endif e, __strides_storage_t(__impl::fill_strides(mdspan_non_standard, s)) #if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) } #else )}) #endif { /* * TODO: check preconditions * - s[i] > 0 is true for all i in the range [0, rank_ ). * - REQUIRED-SPAN-SIZE(e, s) is a representable value of type index_type ([basic.fundamental]). * - If rank_ is greater than 0, then there exists a permutation P of the integers in the * range [0, rank_), such that s[ pi ] >= s[ pi − 1 ] * e.extent( pi − 1 ) is true for * all i in the range [1, rank_ ), where pi is the ith element of P. */ } #ifdef __cpp_lib_span MDSPAN_TEMPLATE_REQUIRES( class IntegralTypes, /* requires */ ( // MSVC 19.32 does not like using index_type here, requires the typename Extents::index_type // error C2641: cannot deduce template arguments for 'MDSPAN_IMPL_STANDARD_NAMESPACE::layout_stride::mapping' _MDSPAN_TRAIT(std::is_convertible, const std::remove_const_t&, typename Extents::index_type) && _MDSPAN_TRAIT(std::is_nothrow_constructible, typename Extents::index_type, const std::remove_const_t&) ) ) MDSPAN_INLINE_FUNCTION constexpr mapping( extents_type const& e, std::span const& s ) noexcept #if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) : __members{ #else : __base_t(__base_t{__member_pair_t( #endif e, __strides_storage_t(__impl::fill_strides(s)) #if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) } #else )}) #endif { /* * TODO: check preconditions * - s[i] > 0 is true for all i in the range [0, rank_ ). * - REQUIRED-SPAN-SIZE(e, s) is a representable value of type index_type ([basic.fundamental]). * - If rank_ is greater than 0, then there exists a permutation P of the integers in the * range [0, rank_), such that s[ pi ] >= s[ pi − 1 ] * e.extent( pi − 1 ) is true for * all i in the range [1, rank_ ), where pi is the ith element of P. */ } #endif // __cpp_lib_span #if !(defined(_MDSPAN_USE_CONCEPTS) && MDSPAN_HAS_CXX_20) MDSPAN_TEMPLATE_REQUIRES( class StridedLayoutMapping, /* requires */ ( _MDSPAN_TRAIT(std::is_constructible, extents_type, typename StridedLayoutMapping::extents_type) && detail::__is_mapping_of && StridedLayoutMapping::is_always_unique() && StridedLayoutMapping::is_always_strided() ) ) #else template requires( detail::__layout_mapping_alike && _MDSPAN_TRAIT(std::is_constructible, extents_type, typename StridedLayoutMapping::extents_type) && StridedLayoutMapping::is_always_unique() && StridedLayoutMapping::is_always_strided() ) #endif MDSPAN_CONDITIONAL_EXPLICIT( !(std::is_convertible::value && (detail::__is_mapping_of || detail::__is_mapping_of || detail::__is_mapping_of)) ) // needs two () due to comma MDSPAN_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 mapping(StridedLayoutMapping const& other) noexcept // NOLINT(google-explicit-constructor) #if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) : __members{ #else : __base_t(__base_t{__member_pair_t( #endif other.extents(), __strides_storage_t(__impl::fill_strides(other)) #if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) } #else )}) #endif { /* * TODO: check preconditions * - other.stride(i) > 0 is true for all i in the range [0, rank_ ). * - other.required_span_size() is a representable value of type index_type ([basic.fundamental]). * - OFFSET(other) == 0 */ } //-------------------------------------------------------------------------------- MDSPAN_INLINE_FUNCTION_DEFAULTED _MDSPAN_CONSTEXPR_14_DEFAULTED mapping& operator=(mapping const&) noexcept = default; MDSPAN_INLINE_FUNCTION constexpr const extents_type& extents() const noexcept { #if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) return __members.__first(); #else return this->__base_t::__ref().__first(); #endif }; MDSPAN_INLINE_FUNCTION constexpr std::array< index_type, extents_type::rank() > strides() const noexcept { return __impl::return_strides(__strides_storage()); } MDSPAN_INLINE_FUNCTION constexpr index_type required_span_size() const noexcept { index_type span_size = 1; for(unsigned r = 0; r < extents_type::rank(); r++) { // Return early if any of the extents are zero if(extents().extent(r)==0) return 0; span_size += ( static_cast(extents().extent(r) - 1 ) * __strides_storage()[r]); } return span_size; } MDSPAN_TEMPLATE_REQUIRES( class... Indices, /* requires */ ( sizeof...(Indices) == Extents::rank() && (detail::are_valid_indices()) ) ) MDSPAN_FORCE_INLINE_FUNCTION constexpr index_type operator()(Indices... idxs) const noexcept { #if ! defined(NDEBUG) detail::check_all_indices(this->extents(), idxs...); #endif // ! NDEBUG return static_cast(__impl::_call_op_impl(*this, static_cast(idxs)...)); } MDSPAN_INLINE_FUNCTION static constexpr bool is_always_unique() noexcept { return true; } MDSPAN_INLINE_FUNCTION static constexpr bool is_always_exhaustive() noexcept { return false; } MDSPAN_INLINE_FUNCTION static constexpr bool is_always_strided() noexcept { return true; } MDSPAN_INLINE_FUNCTION static constexpr bool is_unique() noexcept { return true; } private: constexpr bool exhaustive_for_nonzero_span_size() const { return required_span_size() == __get_size(extents(), std::make_index_sequence()); } constexpr bool is_exhaustive_impl(detail::with_rank<0>) const { return true; } constexpr bool is_exhaustive_impl(detail::with_rank<1>) const { if (required_span_size() != static_cast(0)) { return exhaustive_for_nonzero_span_size(); } return stride(0) == 1; } template constexpr bool is_exhaustive_impl(detail::with_rank) const { if (required_span_size() != static_cast(0)) { return exhaustive_for_nonzero_span_size(); } rank_type r_largest = 0; for (rank_type r = 1; r < extents_type::rank(); r++) { if (stride(r) > stride(r_largest)) { r_largest = r; } } for (rank_type r = 0; r < extents_type::rank(); r++) { if (extents().extent(r) == 0 && r != r_largest) { return false; } } return true; } public: MDSPAN_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 bool is_exhaustive() const noexcept { return is_exhaustive_impl(detail::with_rank{}); } MDSPAN_INLINE_FUNCTION static constexpr bool is_strided() noexcept { return true; } MDSPAN_INLINE_FUNCTION constexpr index_type stride(rank_type r) const noexcept { return __strides_storage()[r]; } #if !(defined(_MDSPAN_USE_CONCEPTS) && MDSPAN_HAS_CXX_20) MDSPAN_TEMPLATE_REQUIRES( class StridedLayoutMapping, /* requires */ ( detail::__is_mapping_of && (extents_type::rank() == StridedLayoutMapping::extents_type::rank()) && StridedLayoutMapping::is_always_strided() ) ) #else template requires( detail::__layout_mapping_alike && (extents_type::rank() == StridedLayoutMapping::extents_type::rank()) && StridedLayoutMapping::is_always_strided() ) #endif MDSPAN_INLINE_FUNCTION friend constexpr bool operator==(const mapping& x, const StridedLayoutMapping& y) noexcept { return (x.extents() == y.extents()) && (__impl::__OFFSET(y) == static_cast(0)) && detail::rankwise_equal(detail::with_rank{}, x, y, detail::stride); } // This one is not technically part of the proposal. Just here to make implementation a bit more optimal hopefully MDSPAN_TEMPLATE_REQUIRES( class OtherExtents, /* requires */ ( (extents_type::rank() == OtherExtents::rank()) ) ) MDSPAN_INLINE_FUNCTION friend constexpr bool operator==(mapping const& lhs, mapping const& rhs) noexcept { return __impl::_eq_impl(lhs, rhs); } #if !MDSPAN_HAS_CXX_20 MDSPAN_TEMPLATE_REQUIRES( class StridedLayoutMapping, /* requires */ ( detail::__is_mapping_of && (extents_type::rank() == StridedLayoutMapping::extents_type::rank()) && StridedLayoutMapping::is_always_strided() ) ) MDSPAN_INLINE_FUNCTION friend constexpr bool operator!=(const mapping& x, const StridedLayoutMapping& y) noexcept { return !(x == y); } MDSPAN_TEMPLATE_REQUIRES( class OtherExtents, /* requires */ ( (extents_type::rank() == OtherExtents::rank()) ) ) MDSPAN_INLINE_FUNCTION friend constexpr bool operator!=(mapping const& lhs, mapping const& rhs) noexcept { return __impl::_not_eq_impl(lhs, rhs); } #endif // [mdspan.submdspan.mapping], submdspan mapping specialization template MDSPAN_INLINE_FUNCTION constexpr auto submdspan_mapping_impl( SliceSpecifiers... slices) const; template friend constexpr auto submdspan_mapping( const mapping& src, SliceSpecifiers... slices) { return src.submdspan_mapping_impl(slices...); } }; }; namespace detail { template constexpr void validate_strides(with_rank<0>, Layout, const Extents&, const Mapping&) {} template constexpr void validate_strides(with_rank, Layout, const Extents& ext, const Mapping& other) { static_assert(std::is_same::value && (std::is_same::value || std::is_same::value) , "This function is only intended to validate construction of " "a layout_left or layout_right mapping from a layout_stride mapping."); constexpr auto is_left = std::is_same::value; typename Extents::index_type expected_stride = 1; for (std::size_t r = 0; r < N; r++) { const std::size_t s = is_left ? r : N - 1 - r; MDSPAN_IMPL_PRECONDITION(common_integral_compare(expected_stride, other.stride(s)) && "invalid strides for layout_{left,right}"); expected_stride *= ext.extent(s); } } } // namespace detail } // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/layout_stride.hpp #if MDSPAN_HAS_CXX_17 //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p2642_bits/layout_padded_fwd.hpp //@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER #include namespace MDSPAN_IMPL_STANDARD_NAMESPACE { namespace MDSPAN_IMPL_PROPOSED_NAMESPACE { template struct layout_left_padded { template class mapping; }; template struct layout_right_padded { template class mapping; }; namespace detail { // The layout_padded_constants structs are only useful if rank > 1, otherwise they may wrap template struct layout_padded_constants; template struct layout_padded_constants, _ExtentsType> { using rank_type = typename _ExtentsType::rank_type; static constexpr rank_type padded_stride_idx = 1; static constexpr rank_type extent_to_pad_idx = 0; }; template struct layout_padded_constants, _ExtentsType> { using rank_type = typename _ExtentsType::rank_type; static constexpr rank_type padded_stride_idx = _ExtentsType::rank() - 2; static constexpr rank_type extent_to_pad_idx = _ExtentsType::rank() - 1; }; template struct is_layout_left_padded : std::false_type {}; template struct is_layout_left_padded> : std::true_type {}; template struct is_layout_left_padded_mapping : std::false_type {}; template struct is_layout_left_padded_mapping<_Mapping, std::enable_if_t::template mapping>::value>> : std::true_type {}; template struct is_layout_right_padded : std::false_type {}; template struct is_layout_right_padded> : std::true_type {}; template struct is_layout_right_padded_mapping : std::false_type {}; template struct is_layout_right_padded_mapping<_Mapping, std::enable_if_t::template mapping>::value>> : std::true_type {}; template constexpr void check_padded_layout_converting_constructor_mandates(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::with_rank<0>) {} template constexpr void check_padded_layout_converting_constructor_mandates(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::with_rank<1>) {} template constexpr void check_padded_layout_converting_constructor_mandates(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::with_rank) { using extents_type = typename _PaddedLayoutMappingType::extents_type; constexpr auto padding_value = _PaddedLayoutMappingType::padding_value; constexpr auto idx = layout_padded_constants::extent_to_pad_idx; constexpr auto statically_determinable = (_LayoutExtentsType::static_extent(idx) != dynamic_extent) && (extents_type::static_extent(idx) != dynamic_extent) && (padding_value != dynamic_extent); static_assert(!statically_determinable || (padding_value == 0 ? _LayoutExtentsType::static_extent(idx) == 0 : _LayoutExtentsType::static_extent(idx) % padding_value == 0), ""); } template constexpr void check_padded_layout_converting_constructor_preconditions(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::with_rank<0>, const _OtherMapping&) {} template constexpr void check_padded_layout_converting_constructor_preconditions(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::with_rank<1>, const _OtherMapping&) {} template constexpr void check_padded_layout_converting_constructor_preconditions(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::with_rank, const _OtherMapping &other_mapping) { constexpr auto padded_stride_idx = layout_padded_constants::padded_stride_idx; constexpr auto extent_to_pad_idx = layout_padded_constants::extent_to_pad_idx; MDSPAN_IMPL_PRECONDITION(other_mapping.stride(padded_stride_idx) == other_mapping.extents().extent(extent_to_pad_idx)); } } } } //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p2642_bits/layout_padded_fwd.hpp #endif namespace MDSPAN_IMPL_STANDARD_NAMESPACE { //============================================================================== template class layout_right::mapping { public: using extents_type = Extents; using index_type = typename extents_type::index_type; using size_type = typename extents_type::size_type; using rank_type = typename extents_type::rank_type; using layout_type = layout_right; private: static_assert(detail::__is_extents_v, MDSPAN_IMPL_STANDARD_NAMESPACE_STRING "::layout_right::mapping must be instantiated with a specialization of " MDSPAN_IMPL_STANDARD_NAMESPACE_STRING "::extents."); template friend class mapping; // i0+(i1 + E(1)*(i2 + E(2)*i3)) template struct __rank_count {}; template _MDSPAN_HOST_DEVICE constexpr index_type __compute_offset( index_type offset, __rank_count, const I& i, Indices... idx) const { return __compute_offset(offset * __extents.extent(r) + i,__rank_count(), idx...); } template _MDSPAN_HOST_DEVICE constexpr index_type __compute_offset( __rank_count<0,extents_type::rank()>, const I& i, Indices... idx) const { return __compute_offset(i,__rank_count<1,extents_type::rank()>(),idx...); } _MDSPAN_HOST_DEVICE constexpr index_type __compute_offset(size_t offset, __rank_count) const { return static_cast(offset); } _MDSPAN_HOST_DEVICE constexpr index_type __compute_offset(__rank_count<0,0>) const { return 0; } public: //-------------------------------------------------------------------------------- MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping() noexcept = default; MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping(mapping const&) noexcept = default; _MDSPAN_HOST_DEVICE constexpr mapping(extents_type const& __exts) noexcept :__extents(__exts) { } MDSPAN_TEMPLATE_REQUIRES( class OtherExtents, /* requires */ ( _MDSPAN_TRAIT(std::is_constructible, extents_type, OtherExtents) ) ) MDSPAN_CONDITIONAL_EXPLICIT((!std::is_convertible::value)) // needs two () due to comma MDSPAN_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 mapping(mapping const& other) noexcept // NOLINT(google-explicit-constructor) :__extents(other.extents()) { /* * TODO: check precondition * other.required_span_size() is a representable value of type index_type */ } MDSPAN_TEMPLATE_REQUIRES( class OtherExtents, /* requires */ ( _MDSPAN_TRAIT(std::is_constructible, extents_type, OtherExtents) && (extents_type::rank() <= 1) ) ) MDSPAN_CONDITIONAL_EXPLICIT((!std::is_convertible::value)) // needs two () due to comma MDSPAN_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 mapping(layout_left::mapping const& other) noexcept // NOLINT(google-explicit-constructor) :__extents(other.extents()) { /* * TODO: check precondition * other.required_span_size() is a representable value of type index_type */ } /** * Converting constructor from `layout_right_padded::mapping`. * * This overload participates in overload resolution only if _Mapping is a layout_right_padded mapping and * extents_type is constructible from _Mapping::extents_type. * * \note There is currently a difference from p2642r2, where this function is specified as taking * `layout_right_padded< padding_value >::mapping< Extents>`. However, this makes `padding_value` non-deducible. */ #if MDSPAN_HAS_CXX_17 MDSPAN_TEMPLATE_REQUIRES( class _Mapping, /* requires */ ( MDSPAN_IMPL_PROPOSED_NAMESPACE::detail::is_layout_right_padded_mapping<_Mapping>::value && std::is_constructible_v)) MDSPAN_CONDITIONAL_EXPLICIT((!std::is_convertible_v)) mapping(const _Mapping &__other) noexcept : __extents(__other.extents()) { MDSPAN_IMPL_PROPOSED_NAMESPACE::detail:: check_padded_layout_converting_constructor_mandates< extents_type, _Mapping>(detail::with_rank{}); MDSPAN_IMPL_PROPOSED_NAMESPACE::detail:: check_padded_layout_converting_constructor_preconditions< extents_type>(detail::with_rank{}, __other); } #endif MDSPAN_TEMPLATE_REQUIRES( class OtherExtents, /* requires */ ( _MDSPAN_TRAIT(std::is_constructible, extents_type, OtherExtents) ) ) MDSPAN_CONDITIONAL_EXPLICIT((extents_type::rank() > 0)) MDSPAN_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 mapping(layout_stride::mapping const& other) noexcept // NOLINT(google-explicit-constructor) :__extents(other.extents()) { /* * TODO: check precondition * other.required_span_size() is a representable value of type index_type */ detail::validate_strides(detail::with_rank{}, layout_right{}, __extents, other); } MDSPAN_INLINE_FUNCTION_DEFAULTED _MDSPAN_CONSTEXPR_14_DEFAULTED mapping& operator=(mapping const&) noexcept = default; MDSPAN_INLINE_FUNCTION constexpr const extents_type& extents() const noexcept { return __extents; } MDSPAN_INLINE_FUNCTION constexpr index_type required_span_size() const noexcept { index_type value = 1; for(rank_type r=0; r != extents_type::rank(); ++r) value*=__extents.extent(r); return value; } //-------------------------------------------------------------------------------- MDSPAN_TEMPLATE_REQUIRES( class ... Indices, /* requires */ ( (sizeof...(Indices) == extents_type::rank()) && (detail::are_valid_indices()) ) ) _MDSPAN_HOST_DEVICE constexpr index_type operator()(Indices... idxs) const noexcept { #if ! defined(NDEBUG) detail::check_all_indices(this->extents(), idxs...); #endif // ! NDEBUG return __compute_offset(__rank_count<0, extents_type::rank()>(), static_cast(idxs)...); } MDSPAN_INLINE_FUNCTION static constexpr bool is_always_unique() noexcept { return true; } MDSPAN_INLINE_FUNCTION static constexpr bool is_always_exhaustive() noexcept { return true; } MDSPAN_INLINE_FUNCTION static constexpr bool is_always_strided() noexcept { return true; } MDSPAN_INLINE_FUNCTION static constexpr bool is_unique() noexcept { return true; } MDSPAN_INLINE_FUNCTION static constexpr bool is_exhaustive() noexcept { return true; } MDSPAN_INLINE_FUNCTION static constexpr bool is_strided() noexcept { return true; } MDSPAN_INLINE_FUNCTION constexpr index_type stride(rank_type i) const noexcept #if MDSPAN_HAS_CXX_20 requires ( Extents::rank() > 0 ) #endif { index_type value = 1; for(rank_type r=extents_type::rank()-1; r>i; r--) value*=__extents.extent(r); return value; } MDSPAN_TEMPLATE_REQUIRES( class OtherExtents, /* requires */ ( Extents::rank() == OtherExtents::rank()) ) MDSPAN_INLINE_FUNCTION friend constexpr bool operator==(mapping const& lhs, mapping const& rhs) noexcept { return lhs.extents() == rhs.extents(); } // In C++ 20 the not equal exists if equal is found #if !(MDSPAN_HAS_CXX_20) MDSPAN_TEMPLATE_REQUIRES( class OtherExtents, /* requires */ (Extents::rank() == OtherExtents::rank()) ) MDSPAN_INLINE_FUNCTION friend constexpr bool operator!=(mapping const& lhs, mapping const& rhs) noexcept { return lhs.extents() != rhs.extents(); } #endif // Not really public, but currently needed to implement fully constexpr useable submdspan: template constexpr index_type __get_stride(MDSPAN_IMPL_STANDARD_NAMESPACE::extents,std::integer_sequence) const { return _MDSPAN_FOLD_TIMES_RIGHT((Idx>N? __extents.template __extent():1),1); } template constexpr index_type __stride() const noexcept { return __get_stride(__extents, std::make_index_sequence()); } private: _MDSPAN_NO_UNIQUE_ADDRESS extents_type __extents{}; // [mdspan.submdspan.mapping], submdspan mapping specialization template MDSPAN_INLINE_FUNCTION constexpr auto submdspan_mapping_impl( SliceSpecifiers... slices) const; template friend constexpr auto submdspan_mapping( const mapping& src, SliceSpecifiers... slices) { return src.submdspan_mapping_impl(slices...); } }; } // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/layout_right.hpp namespace MDSPAN_IMPL_STANDARD_NAMESPACE { template < class ElementType, class Extents, class LayoutPolicy = layout_right, class AccessorPolicy = default_accessor > class mdspan { private: static_assert(detail::__is_extents_v, MDSPAN_IMPL_STANDARD_NAMESPACE_STRING "::mdspan's Extents template parameter must be a specialization of " MDSPAN_IMPL_STANDARD_NAMESPACE_STRING "::extents."); static_assert(std::is_same::value, MDSPAN_IMPL_STANDARD_NAMESPACE_STRING "::mdspan's ElementType template parameter must be the same as its AccessorPolicy::element_type."); // Workaround for non-deducibility of the index sequence template parameter if it's given at the top level template struct __deduction_workaround; template struct __deduction_workaround> { MDSPAN_FORCE_INLINE_FUNCTION static constexpr size_t __size(mdspan const& __self) noexcept { return _MDSPAN_FOLD_TIMES_RIGHT((__self.__mapping_ref().extents().extent(Idxs)), /* * ... * */ size_t(1)); } MDSPAN_FORCE_INLINE_FUNCTION static constexpr bool __empty(mdspan const& __self) noexcept { return (__self.rank()>0) && _MDSPAN_FOLD_OR((__self.__mapping_ref().extents().extent(Idxs)==index_type(0))); } template MDSPAN_FORCE_INLINE_FUNCTION static constexpr ReferenceType __callop(mdspan const& __self, const std::array& indices) noexcept { return __self.__accessor_ref().access(__self.__ptr_ref(), __self.__mapping_ref()(indices[Idxs]...)); } #ifdef __cpp_lib_span template MDSPAN_FORCE_INLINE_FUNCTION static constexpr ReferenceType __callop(mdspan const& __self, const std::span& indices) noexcept { return __self.__accessor_ref().access(__self.__ptr_ref(), __self.__mapping_ref()(indices[Idxs]...)); } #endif }; public: //-------------------------------------------------------------------------------- // Domain and codomain types using extents_type = Extents; using layout_type = LayoutPolicy; using accessor_type = AccessorPolicy; using mapping_type = typename layout_type::template mapping; using element_type = ElementType; using value_type = std::remove_cv_t; using index_type = typename extents_type::index_type; using size_type = typename extents_type::size_type; using rank_type = typename extents_type::rank_type; using data_handle_type = typename accessor_type::data_handle_type; using reference = typename accessor_type::reference; MDSPAN_INLINE_FUNCTION static constexpr size_t rank() noexcept { return extents_type::rank(); } MDSPAN_INLINE_FUNCTION static constexpr size_t rank_dynamic() noexcept { return extents_type::rank_dynamic(); } MDSPAN_INLINE_FUNCTION static constexpr size_t static_extent(size_t r) noexcept { return extents_type::static_extent(r); } MDSPAN_INLINE_FUNCTION constexpr index_type extent(size_t r) const noexcept { return __mapping_ref().extents().extent(r); }; private: // Can't use defaulted parameter in the __deduction_workaround template because of a bug in MSVC warning C4348. using __impl = __deduction_workaround>; using __map_acc_pair_t = detail::__compressed_pair; public: //-------------------------------------------------------------------------------- // [mdspan.basic.cons], mdspan constructors, assignment, and destructor #if !MDSPAN_HAS_CXX_20 MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mdspan() = default; #else MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mdspan() requires( // nvhpc has a bug where using just rank_dynamic() here doesn't work ... (extents_type::rank_dynamic() > 0) && _MDSPAN_TRAIT(std::is_default_constructible, data_handle_type) && _MDSPAN_TRAIT(std::is_default_constructible, mapping_type) && _MDSPAN_TRAIT(std::is_default_constructible, accessor_type) ) = default; #endif MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mdspan(const mdspan&) = default; MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mdspan(mdspan&&) = default; MDSPAN_TEMPLATE_REQUIRES( class... SizeTypes, /* requires */ ( ((sizeof...(SizeTypes) == rank()) || (sizeof...(SizeTypes) == rank_dynamic())) && (detail::are_valid_indices()) && _MDSPAN_TRAIT(std::is_constructible, mapping_type, extents_type) && _MDSPAN_TRAIT(std::is_default_constructible, accessor_type) ) ) MDSPAN_INLINE_FUNCTION explicit constexpr mdspan(data_handle_type p, SizeTypes... dynamic_extents) // TODO @proposal-bug shouldn't I be allowed to do `move(p)` here? : __members(std::move(p), __map_acc_pair_t(mapping_type(extents_type(static_cast(std::move(dynamic_extents))...)), accessor_type())) { } MDSPAN_TEMPLATE_REQUIRES( class SizeType, size_t N, /* requires */ ( _MDSPAN_TRAIT(std::is_convertible, const SizeType&, index_type) && _MDSPAN_TRAIT(std::is_nothrow_constructible, index_type, const SizeType&) && ((N == rank()) || (N == rank_dynamic())) && _MDSPAN_TRAIT(std::is_constructible, mapping_type, extents_type) && _MDSPAN_TRAIT(std::is_default_constructible, accessor_type) ) ) MDSPAN_CONDITIONAL_EXPLICIT(N != rank_dynamic()) MDSPAN_INLINE_FUNCTION constexpr mdspan(data_handle_type p, const std::array& dynamic_extents) : __members(std::move(p), __map_acc_pair_t(mapping_type(extents_type(dynamic_extents)), accessor_type())) { } #ifdef __cpp_lib_span MDSPAN_TEMPLATE_REQUIRES( class SizeType, size_t N, /* requires */ ( _MDSPAN_TRAIT(std::is_convertible, const SizeType&, index_type) && _MDSPAN_TRAIT(std::is_nothrow_constructible, index_type, const SizeType&) && ((N == rank()) || (N == rank_dynamic())) && _MDSPAN_TRAIT(std::is_constructible, mapping_type, extents_type) && _MDSPAN_TRAIT(std::is_default_constructible, accessor_type) ) ) MDSPAN_CONDITIONAL_EXPLICIT(N != rank_dynamic()) MDSPAN_INLINE_FUNCTION constexpr mdspan(data_handle_type p, std::span dynamic_extents) : __members(std::move(p), __map_acc_pair_t(mapping_type(extents_type(as_const(dynamic_extents))), accessor_type())) { } #endif MDSPAN_FUNCTION_REQUIRES( (MDSPAN_INLINE_FUNCTION constexpr), mdspan, (data_handle_type p, const extents_type& exts), , /* requires */ (_MDSPAN_TRAIT(std::is_default_constructible, accessor_type) && _MDSPAN_TRAIT(std::is_constructible, mapping_type, const extents_type&)) ) : __members(std::move(p), __map_acc_pair_t(mapping_type(exts), accessor_type())) { } MDSPAN_FUNCTION_REQUIRES( (MDSPAN_INLINE_FUNCTION constexpr), mdspan, (data_handle_type p, const mapping_type& m), , /* requires */ (_MDSPAN_TRAIT(std::is_default_constructible, accessor_type)) ) : __members(std::move(p), __map_acc_pair_t(m, accessor_type())) { } MDSPAN_INLINE_FUNCTION constexpr mdspan(data_handle_type p, const mapping_type& m, const accessor_type& a) : __members(std::move(p), __map_acc_pair_t(m, a)) { } MDSPAN_TEMPLATE_REQUIRES( class OtherElementType, class OtherExtents, class OtherLayoutPolicy, class OtherAccessor, /* requires */ ( _MDSPAN_TRAIT(std::is_constructible, mapping_type, const typename OtherLayoutPolicy::template mapping&) && _MDSPAN_TRAIT(std::is_constructible, accessor_type, const OtherAccessor&) ) ) MDSPAN_CONDITIONAL_EXPLICIT( !_MDSPAN_TRAIT(std::is_convertible, const typename OtherLayoutPolicy::template mapping&, mapping_type) || !_MDSPAN_TRAIT(std::is_convertible, const OtherAccessor&, accessor_type) ) MDSPAN_INLINE_FUNCTION constexpr mdspan(const mdspan& other) : __members(other.__ptr_ref(), __map_acc_pair_t(other.__mapping_ref(), other.__accessor_ref())) { static_assert(_MDSPAN_TRAIT(std::is_constructible, data_handle_type, typename OtherAccessor::data_handle_type),"Incompatible data_handle_type for mdspan construction"); static_assert(_MDSPAN_TRAIT(std::is_constructible, extents_type, OtherExtents),"Incompatible extents for mdspan construction"); /* * TODO: Check precondition * For each rank index r of extents_type, static_extent(r) == dynamic_extent || static_extent(r) == other.extent(r) is true. */ } /* Might need this on NVIDIA? MDSPAN_INLINE_FUNCTION_DEFAULTED ~mdspan() = default; */ MDSPAN_INLINE_FUNCTION_DEFAULTED _MDSPAN_CONSTEXPR_14_DEFAULTED mdspan& operator=(const mdspan&) = default; MDSPAN_INLINE_FUNCTION_DEFAULTED _MDSPAN_CONSTEXPR_14_DEFAULTED mdspan& operator=(mdspan&&) = default; //-------------------------------------------------------------------------------- // [mdspan.basic.mapping], mdspan mapping domain multidimensional index to access codomain element #if MDSPAN_USE_BRACKET_OPERATOR MDSPAN_TEMPLATE_REQUIRES( class... SizeTypes, /* requires */ ( _MDSPAN_FOLD_AND(_MDSPAN_TRAIT(std::is_convertible, SizeTypes, index_type) /* && ... */) && _MDSPAN_FOLD_AND(_MDSPAN_TRAIT(std::is_nothrow_constructible, index_type, SizeTypes) /* && ... */) && (rank() == sizeof...(SizeTypes)) ) ) MDSPAN_FORCE_INLINE_FUNCTION constexpr reference operator[](SizeTypes... indices) const { return __accessor_ref().access(__ptr_ref(), __mapping_ref()(static_cast(std::move(indices))...)); } #endif MDSPAN_TEMPLATE_REQUIRES( class SizeType, /* requires */ ( _MDSPAN_TRAIT(std::is_convertible, const SizeType&, index_type) && _MDSPAN_TRAIT(std::is_nothrow_constructible, index_type, const SizeType&) ) ) MDSPAN_FORCE_INLINE_FUNCTION constexpr reference operator[](const std::array< SizeType, rank()>& indices) const { return __impl::template __callop(*this, indices); } #ifdef __cpp_lib_span MDSPAN_TEMPLATE_REQUIRES( class SizeType, /* requires */ ( _MDSPAN_TRAIT(std::is_convertible, const SizeType&, index_type) && _MDSPAN_TRAIT(std::is_nothrow_constructible, index_type, const SizeType&) ) ) MDSPAN_FORCE_INLINE_FUNCTION constexpr reference operator[](std::span indices) const { return __impl::template __callop(*this, indices); } #endif // __cpp_lib_span #if !MDSPAN_USE_BRACKET_OPERATOR MDSPAN_TEMPLATE_REQUIRES( class Index, /* requires */ ( _MDSPAN_TRAIT(std::is_convertible, Index, index_type) && _MDSPAN_TRAIT(std::is_nothrow_constructible, index_type, Index) && extents_type::rank() == 1 ) ) MDSPAN_FORCE_INLINE_FUNCTION constexpr reference operator[](Index idx) const { return __accessor_ref().access(__ptr_ref(), __mapping_ref()(static_cast(std::move(idx)))); } #endif #if MDSPAN_USE_PAREN_OPERATOR MDSPAN_TEMPLATE_REQUIRES( class... SizeTypes, /* requires */ ( extents_type::rank() == sizeof...(SizeTypes) && (detail::are_valid_indices()) ) ) MDSPAN_FORCE_INLINE_FUNCTION constexpr reference operator()(SizeTypes... indices) const { return __accessor_ref().access(__ptr_ref(), __mapping_ref()(static_cast(std::move(indices))...)); } MDSPAN_TEMPLATE_REQUIRES( class SizeType, /* requires */ ( _MDSPAN_TRAIT(std::is_convertible, const SizeType&, index_type) && _MDSPAN_TRAIT(std::is_nothrow_constructible, index_type, const SizeType&) ) ) MDSPAN_FORCE_INLINE_FUNCTION constexpr reference operator()(const std::array& indices) const { return __impl::template __callop(*this, indices); } #ifdef __cpp_lib_span MDSPAN_TEMPLATE_REQUIRES( class SizeType, /* requires */ ( _MDSPAN_TRAIT(std::is_convertible, const SizeType&, index_type) && _MDSPAN_TRAIT(std::is_nothrow_constructible, index_type, const SizeType&) ) ) MDSPAN_FORCE_INLINE_FUNCTION constexpr reference operator()(std::span indices) const { return __impl::template __callop(*this, indices); } #endif // __cpp_lib_span #endif // MDSPAN_USE_PAREN_OPERATOR MDSPAN_INLINE_FUNCTION constexpr size_type size() const noexcept { return static_cast(__impl::__size(*this)); }; MDSPAN_INLINE_FUNCTION constexpr bool empty() const noexcept { return __impl::__empty(*this); }; MDSPAN_INLINE_FUNCTION friend constexpr void swap(mdspan& x, mdspan& y) noexcept { // can't call the std::swap inside on HIP #if !defined(_MDSPAN_HAS_HIP) && !defined(_MDSPAN_HAS_CUDA) using std::swap; swap(x.__ptr_ref(), y.__ptr_ref()); swap(x.__mapping_ref(), y.__mapping_ref()); swap(x.__accessor_ref(), y.__accessor_ref()); #else mdspan tmp = y; y = x; x = tmp; #endif } //-------------------------------------------------------------------------------- // [mdspan.basic.domobs], mdspan observers of the domain multidimensional index space MDSPAN_INLINE_FUNCTION constexpr const extents_type& extents() const noexcept { return __mapping_ref().extents(); }; MDSPAN_INLINE_FUNCTION constexpr const data_handle_type& data_handle() const noexcept { return __ptr_ref(); }; MDSPAN_INLINE_FUNCTION constexpr const mapping_type& mapping() const noexcept { return __mapping_ref(); }; MDSPAN_INLINE_FUNCTION constexpr const accessor_type& accessor() const noexcept { return __accessor_ref(); }; //-------------------------------------------------------------------------------- // [mdspan.basic.obs], mdspan observers of the mapping MDSPAN_INLINE_FUNCTION static constexpr bool is_always_unique() { return mapping_type::is_always_unique(); }; MDSPAN_INLINE_FUNCTION static constexpr bool is_always_exhaustive() { return mapping_type::is_always_exhaustive(); }; MDSPAN_INLINE_FUNCTION static constexpr bool is_always_strided() { return mapping_type::is_always_strided(); }; MDSPAN_INLINE_FUNCTION constexpr bool is_unique() const { return __mapping_ref().is_unique(); }; MDSPAN_INLINE_FUNCTION constexpr bool is_exhaustive() const { return __mapping_ref().is_exhaustive(); }; MDSPAN_INLINE_FUNCTION constexpr bool is_strided() const { return __mapping_ref().is_strided(); }; MDSPAN_INLINE_FUNCTION constexpr index_type stride(size_t r) const { return __mapping_ref().stride(r); }; private: detail::__compressed_pair __members{}; MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 data_handle_type& __ptr_ref() noexcept { return __members.__first(); } MDSPAN_FORCE_INLINE_FUNCTION constexpr data_handle_type const& __ptr_ref() const noexcept { return __members.__first(); } MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 mapping_type& __mapping_ref() noexcept { return __members.__second().__first(); } MDSPAN_FORCE_INLINE_FUNCTION constexpr mapping_type const& __mapping_ref() const noexcept { return __members.__second().__first(); } MDSPAN_FORCE_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 accessor_type& __accessor_ref() noexcept { return __members.__second().__second(); } MDSPAN_FORCE_INLINE_FUNCTION constexpr accessor_type const& __accessor_ref() const noexcept { return __members.__second().__second(); } template friend class mdspan; }; #if defined(_MDSPAN_USE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION) MDSPAN_TEMPLATE_REQUIRES( class ElementType, class... SizeTypes, /* requires */ _MDSPAN_FOLD_AND(_MDSPAN_TRAIT(std::is_convertible, SizeTypes, size_t) /* && ... */) && (sizeof...(SizeTypes) > 0) ) MDSPAN_DEDUCTION_GUIDE explicit mdspan(ElementType*, SizeTypes...) -> mdspan>; MDSPAN_TEMPLATE_REQUIRES( class Pointer, (_MDSPAN_TRAIT(std::is_pointer, std::remove_reference_t)) ) MDSPAN_DEDUCTION_GUIDE mdspan(Pointer&&) -> mdspan>, extents>; MDSPAN_TEMPLATE_REQUIRES( class CArray, (_MDSPAN_TRAIT(std::is_array, CArray) && (std::rank_v == 1)) ) MDSPAN_DEDUCTION_GUIDE mdspan(CArray&) -> mdspan, extents>>; template MDSPAN_DEDUCTION_GUIDE mdspan(ElementType*, const ::std::array&) -> mdspan>; #ifdef __cpp_lib_span template MDSPAN_DEDUCTION_GUIDE mdspan(ElementType*, ::std::span) -> mdspan>; #endif // This one is necessary because all the constructors take `data_handle_type`s, not // `ElementType*`s, and `data_handle_type` is taken from `accessor_type::data_handle_type`, which // seems to throw off automatic deduction guides. template MDSPAN_DEDUCTION_GUIDE mdspan(ElementType*, const extents&) -> mdspan>; template MDSPAN_DEDUCTION_GUIDE mdspan(ElementType*, const MappingType&) -> mdspan; template MDSPAN_DEDUCTION_GUIDE mdspan(const typename AccessorType::data_handle_type, const MappingType&, const AccessorType&) -> mdspan; #endif } // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/mdspan.hpp //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/layout_left.hpp //@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // See https://kokkos.org/LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER #if MDSPAN_HAS_CXX_17 #endif #include namespace MDSPAN_IMPL_STANDARD_NAMESPACE { //============================================================================== template class layout_left::mapping { public: using extents_type = Extents; using index_type = typename extents_type::index_type; using size_type = typename extents_type::size_type; using rank_type = typename extents_type::rank_type; using layout_type = layout_left; private: static_assert(detail::__is_extents_v, MDSPAN_IMPL_STANDARD_NAMESPACE_STRING "::layout_left::mapping must be instantiated with a specialization of " MDSPAN_IMPL_STANDARD_NAMESPACE_STRING "::extents."); template friend class mapping; // i0+(i1 + E(1)*(i2 + E(2)*i3)) template struct __rank_count {}; template _MDSPAN_HOST_DEVICE constexpr index_type __compute_offset( __rank_count, const I& i, Indices... idx) const { return __compute_offset(__rank_count(), idx...) * __extents.extent(r) + i; } template _MDSPAN_HOST_DEVICE constexpr index_type __compute_offset( __rank_count, const I& i) const { return i; } _MDSPAN_HOST_DEVICE constexpr index_type __compute_offset(__rank_count<0,0>) const { return 0; } public: //-------------------------------------------------------------------------------- MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping() noexcept = default; MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping(mapping const&) noexcept = default; _MDSPAN_HOST_DEVICE constexpr mapping(extents_type const& __exts) noexcept :__extents(__exts) { } MDSPAN_TEMPLATE_REQUIRES( class OtherExtents, /* requires */ ( _MDSPAN_TRAIT(std::is_constructible, extents_type, OtherExtents) ) ) MDSPAN_CONDITIONAL_EXPLICIT((!std::is_convertible::value)) // needs two () due to comma MDSPAN_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 mapping(mapping const& other) noexcept // NOLINT(google-explicit-constructor) :__extents(other.extents()) { /* * TODO: check precondition * other.required_span_size() is a representable value of type index_type */ } MDSPAN_TEMPLATE_REQUIRES( class OtherExtents, /* requires */ ( _MDSPAN_TRAIT(std::is_constructible, extents_type, OtherExtents) && (extents_type::rank() <= 1) ) ) MDSPAN_CONDITIONAL_EXPLICIT((!std::is_convertible::value)) // needs two () due to comma MDSPAN_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 mapping(layout_right::mapping const& other) noexcept // NOLINT(google-explicit-constructor) :__extents(other.extents()) { /* * TODO: check precondition * other.required_span_size() is a representable value of type index_type */ } #if MDSPAN_HAS_CXX_17 /** * Converting constructor from `layout_left_padded::mapping`. * * This overload participates in overload resolution only if _Mapping is a layout_left_padded mapping and * extents_type is constructible from _Mapping::extents_type. * * \note There is currently a difference from p2642r2, where this function is specified as taking * `layout_left_padded< padding_value >::mapping< Extents>`. However, this makes `padding_value` non-deducible. */ MDSPAN_TEMPLATE_REQUIRES( class _Mapping, /* requires */ ( MDSPAN_IMPL_PROPOSED_NAMESPACE::detail::is_layout_left_padded_mapping<_Mapping>::value && std::is_constructible_v ) ) MDSPAN_CONDITIONAL_EXPLICIT((!std::is_convertible_v)) mapping(const _Mapping& __other) noexcept : __extents(__other.extents()) { MDSPAN_IMPL_PROPOSED_NAMESPACE::detail:: check_padded_layout_converting_constructor_mandates< extents_type, _Mapping>(detail::with_rank{}); MDSPAN_IMPL_PROPOSED_NAMESPACE::detail:: check_padded_layout_converting_constructor_preconditions< extents_type>(detail::with_rank{}, __other); } #endif MDSPAN_TEMPLATE_REQUIRES( class OtherExtents, /* requires */ ( _MDSPAN_TRAIT(std::is_constructible, extents_type, OtherExtents) ) ) MDSPAN_CONDITIONAL_EXPLICIT((extents_type::rank() > 0)) MDSPAN_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 mapping(layout_stride::mapping const& other) noexcept // NOLINT(google-explicit-constructor) :__extents(other.extents()) { /* * TODO: check precondition * other.required_span_size() is a representable value of type index_type */ detail::validate_strides(detail::with_rank{}, layout_left{}, __extents, other); } MDSPAN_INLINE_FUNCTION_DEFAULTED _MDSPAN_CONSTEXPR_14_DEFAULTED mapping& operator=(mapping const&) noexcept = default; MDSPAN_INLINE_FUNCTION constexpr const extents_type& extents() const noexcept { return __extents; } MDSPAN_INLINE_FUNCTION constexpr index_type required_span_size() const noexcept { index_type value = 1; for(rank_type r=0; r()) ) ) _MDSPAN_HOST_DEVICE constexpr index_type operator()(Indices... idxs) const noexcept { #if ! defined(NDEBUG) detail::check_all_indices(this->extents(), idxs...); #endif // ! NDEBUG return __compute_offset(__rank_count<0, extents_type::rank()>(), static_cast(idxs)...); } MDSPAN_INLINE_FUNCTION static constexpr bool is_always_unique() noexcept { return true; } MDSPAN_INLINE_FUNCTION static constexpr bool is_always_exhaustive() noexcept { return true; } MDSPAN_INLINE_FUNCTION static constexpr bool is_always_strided() noexcept { return true; } MDSPAN_INLINE_FUNCTION static constexpr bool is_unique() noexcept { return true; } MDSPAN_INLINE_FUNCTION static constexpr bool is_exhaustive() noexcept { return true; } MDSPAN_INLINE_FUNCTION static constexpr bool is_strided() noexcept { return true; } MDSPAN_INLINE_FUNCTION constexpr index_type stride(rank_type i) const noexcept #if MDSPAN_HAS_CXX_20 requires ( Extents::rank() > 0 ) #endif { index_type value = 1; for(rank_type r=0; r const& rhs) noexcept { return lhs.extents() == rhs.extents(); } // In C++ 20 the not equal exists if equal is found #if !(MDSPAN_HAS_CXX_20) MDSPAN_TEMPLATE_REQUIRES( class OtherExtents, /* requires */ ( Extents::rank() == OtherExtents::rank()) ) MDSPAN_INLINE_FUNCTION friend constexpr bool operator!=(mapping const& lhs, mapping const& rhs) noexcept { return lhs.extents() != rhs.extents(); } #endif // Not really public, but currently needed to implement fully constexpr useable submdspan: template constexpr index_type __get_stride(MDSPAN_IMPL_STANDARD_NAMESPACE::extents,std::integer_sequence) const { return _MDSPAN_FOLD_TIMES_RIGHT((Idx():1),1); } template constexpr index_type __stride() const noexcept { return __get_stride(__extents, std::make_index_sequence()); } private: _MDSPAN_NO_UNIQUE_ADDRESS extents_type __extents{}; // [mdspan.submdspan.mapping], submdspan mapping specialization template MDSPAN_INLINE_FUNCTION constexpr auto submdspan_mapping_impl( SliceSpecifiers... slices) const; template friend constexpr auto submdspan_mapping( const mapping& src, SliceSpecifiers... slices) { return src.submdspan_mapping_impl(slices...); } }; } // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p0009_bits/layout_left.hpp #if MDSPAN_HAS_CXX_17 //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p2642_bits/layout_padded.hpp //@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER #include namespace MDSPAN_IMPL_STANDARD_NAMESPACE { namespace MDSPAN_IMPL_PROPOSED_NAMESPACE { namespace detail { template MDSPAN_INLINE_FUNCTION constexpr _T find_next_multiple(_T alignment, _T offset) { if ( alignment == 0 ) { return _T(0); } else { return ( ( offset + alignment - 1 ) / alignment) * alignment; } } template MDSPAN_INLINE_FUNCTION constexpr size_t get_actual_static_padding_value() { constexpr auto rank = _ExtentsType::rank(); if constexpr (rank <= typename _ExtentsType::rank_type(1)) { return 0; } else if constexpr (_PaddingValue != dynamic_extent && _ExtentsType::static_extent(_ExtentToPadIdx) != dynamic_extent) { static_assert( (_PaddingValue != 0) || (_ExtentsType::static_extent(_ExtentToPadIdx) == 0), "padding stride can be 0 only if " "extents_type::static_extent(extent-to-pad) is 0 or dynamic_extent"); return find_next_multiple(_PaddingValue, _ExtentsType::static_extent(_ExtentToPadIdx)); } else { return dynamic_extent; } // Missing return statement warning from NVCC and ICC #if defined(__NVCC__) || defined(__INTEL_COMPILER) return 0; #endif } template struct static_array_type_for_padded_extent { static constexpr size_t padding_value = _PaddingValue; using index_type = typename _Extents::index_type; using extents_type = _Extents; using type = ::MDSPAN_IMPL_STANDARD_NAMESPACE::detail::maybe_static_array< index_type, size_t, dynamic_extent, ::MDSPAN_IMPL_STANDARD_NAMESPACE::MDSPAN_IMPL_PROPOSED_NAMESPACE::detail::get_actual_static_padding_value()>; }; template struct static_array_type_for_padded_extent<_PaddingValue, _Extents, _ExtentToPadIdx, Rank, std::enable_if_t> { using index_type = typename _Extents::index_type; using extents_type = _Extents; using type = ::MDSPAN_IMPL_STANDARD_NAMESPACE::detail::maybe_static_array< index_type, size_t, dynamic_extent, 0>; }; template struct padded_extent { static constexpr size_t padding_value = _PaddingValue; using index_type = typename _Extents::index_type; using extents_type = _Extents; using static_array_type = typename static_array_type_for_padded_extent< padding_value, _Extents, _ExtentToPadIdx, _Extents::rank()>::type; static constexpr auto static_value() { return static_array_type::static_value(0); } MDSPAN_INLINE_FUNCTION static constexpr static_array_type init_padding(const _Extents &exts) { if constexpr ((_Extents::rank() > 1) && (padding_value == dynamic_extent)) { return {exts.extent(_ExtentToPadIdx)}; } else { return init_padding(exts, padding_value); } // Missing return statement warning from NVCC and ICC #if defined(__NVCC__) || defined(__INTEL_COMPILER) return {}; #endif } MDSPAN_INLINE_FUNCTION static constexpr static_array_type init_padding([[maybe_unused]] const _Extents &exts, [[maybe_unused]] index_type pv) { if constexpr (_Extents::rank() > 1) { return {find_next_multiple(pv, exts.extent(_ExtentToPadIdx))}; } else { return {}; } // Missing return statement warning from NVCC and ICC #if defined(__NVCC__) || defined(__INTEL_COMPILER) return {}; #endif } template MDSPAN_INLINE_FUNCTION static constexpr static_array_type init_padding([[maybe_unused]] const _Mapping &other_mapping, std::integral_constant) { if constexpr (_Extents::rank() > 1) { return {other_mapping.stride(_PaddingStrideIdx)}; } else { return {}; } // Missing return statement warning from NVCC and ICC #if defined(__NVCC__) || defined(__INTEL_COMPILER) return {}; #endif } }; } // namespace detail template template class layout_left_padded::mapping { public: static constexpr size_t padding_value = PaddingValue; using extents_type = Extents; using index_type = typename extents_type::index_type; using size_type = typename extents_type::size_type; using rank_type = typename extents_type::rank_type; using layout_type = layout_left_padded; #ifndef MDSPAN_INTERNAL_TEST private: #endif // MDSPAN_INTERNAL_TEST static constexpr rank_type padded_stride_idx = detail::layout_padded_constants::padded_stride_idx; static constexpr rank_type extent_to_pad_idx = detail::layout_padded_constants::extent_to_pad_idx; static_assert((padding_value != 0) || (extents_type::static_extent(extent_to_pad_idx) == 0) || (extents_type::static_extent(extent_to_pad_idx) == dynamic_extent), "out of bounds access for rank 0"); using padded_stride_type = detail::padded_extent< padding_value, extents_type, extent_to_pad_idx >; static constexpr size_t static_padding_stride = padded_stride_type::static_value(); typename padded_stride_type::static_array_type padded_stride = {}; extents_type exts = {}; MDSPAN_INLINE_FUNCTION constexpr index_type compute_offset(std::index_sequence<>) const { return 0; } template MDSPAN_INLINE_FUNCTION constexpr index_type compute_offset(std::index_sequence, IndexOffset index_offset) const { return index_offset; } template MDSPAN_INLINE_FUNCTION constexpr index_type compute_offset(std::index_sequence, IndexOffsets... index_offsets) const { index_type indices[] = {static_cast(index_offsets)...}; // self-recursive fold trick from // https://github.com/llvm/llvm-project/blob/96e1914aa2e6d8966acbfbe2f4d184201f1aa318/libcxx/include/mdspan/layout_left.h#L144 index_type res = 0; ((res = indices[extents_type::rank() - 1 - Ranks] + ((extents_type::rank() - 1 - Ranks) == extent_to_pad_idx ? padded_stride.value(0) : exts.extent(extents_type::rank() - 1 - Ranks)) * res), ...); return res; } public: #if !MDSPAN_HAS_CXX_20 MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping() : mapping(extents_type{}) {} #else MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping() requires(static_padding_stride != dynamic_extent) = default; MDSPAN_INLINE_FUNCTION constexpr mapping() requires(static_padding_stride == dynamic_extent) : mapping(extents_type{}) {} #endif MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping(const mapping&) noexcept = default; MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping& operator=(const mapping&) noexcept = default; /** * Initializes the mapping with the given extents. * * \param ext the given extents */ MDSPAN_INLINE_FUNCTION constexpr mapping(const extents_type& ext) : padded_stride(padded_stride_type::init_padding(ext)), exts(ext) {} /** * Initializes the mapping with the given extents and the specified padding value. * * This overload participates in overload resolution only if `is_convertible_v` * is `true` and `is_nothrow_constructible_v` is `true` * * \param ext the given extents * \param padding_value the padding value */ MDSPAN_TEMPLATE_REQUIRES( class _Size, /* requires */ ( std::is_convertible_v<_Size, index_type> && std::is_nothrow_constructible_v ) ) MDSPAN_INLINE_FUNCTION constexpr mapping(const extents_type &ext, _Size dynamic_padding_value) : padded_stride(padded_stride_type::init_padding(ext, dynamic_padding_value)), exts(ext) { assert((padding_value == dynamic_extent) || (static_cast(padding_value) == static_cast(dynamic_padding_value))); } /** * Converting constructor from `layout_left::mapping`. * * This overload participates in overload resolution only if * `is_constructible_v` is true. If * `OtherExtents::rank() > 1` then one of `padding_value`, `static_extent(0)`, * or `OtherExtents::static_extent(0)` must be `dynamic_extent`; otherwise, * `OtherExtents::static_extent(0)` must be equal to the least multiple of * `padding_value` greater than or equal to `extents_type::static_extent(0)` */ MDSPAN_TEMPLATE_REQUIRES( class _OtherExtents, /* requires */ (std::is_constructible_v)) MDSPAN_CONDITIONAL_EXPLICIT( (!std::is_convertible_v<_OtherExtents, extents_type>)) MDSPAN_INLINE_FUNCTION constexpr mapping(const layout_left::mapping<_OtherExtents> &other_mapping) : padded_stride(padded_stride_type::init_padding( other_mapping, std::integral_constant{})), exts(other_mapping.extents()) { static_assert( (_OtherExtents::rank() > 1) || (static_padding_stride != dynamic_extent) || (_OtherExtents::static_extent(extent_to_pad_idx) != dynamic_extent) || (static_padding_stride == _OtherExtents::static_extent(extent_to_pad_idx))); } /** * Converting constructor from `layout_stride::mapping`. * * This overload participates in overload resolution only if * `is_constructible_v` is true */ MDSPAN_TEMPLATE_REQUIRES( class _OtherExtents, /* requires */ (std::is_constructible_v)) MDSPAN_CONDITIONAL_EXPLICIT((extents_type::rank() > 0)) MDSPAN_INLINE_FUNCTION constexpr mapping(const layout_stride::mapping<_OtherExtents> &other_mapping) : padded_stride(padded_stride_type::init_padding( other_mapping, std::integral_constant{})), exts(other_mapping.extents()) {} /** * Converting constructor from `layout_left_padded::mapping`. * * This overload participates in overload resolution only if * `is_constructible_v` is true. Either * `padding_value` or `OtherPaddingStride` must be `std::dynamic_extent`, or * `padding_value == OtherPaddingStride`. */ MDSPAN_TEMPLATE_REQUIRES( class _Mapping, /* requires */ (detail::is_layout_left_padded_mapping<_Mapping>::value &&std::is_constructible_v< extents_type, typename _Mapping::extents_type>)) MDSPAN_CONDITIONAL_EXPLICIT((extents_type::rank() > 1 && (padding_value == dynamic_extent || _Mapping::padding_value == dynamic_extent))) MDSPAN_INLINE_FUNCTION constexpr mapping(const _Mapping &other_mapping) : padded_stride(padded_stride_type::init_padding( other_mapping, std::integral_constant{})), exts(other_mapping.extents()) { static_assert(padding_value == dynamic_extent || _Mapping::padding_value == dynamic_extent || padding_value == _Mapping::padding_value); } /** * Converting constructor from `layout_right_padded::mapping`. * * This overload participates in overload resolution only if * `extents_type::rank()` is 0 or 1 and `is_constructible_v` is `true`. */ MDSPAN_TEMPLATE_REQUIRES( class _Mapping, /* requires */ (detail::is_layout_right_padded_mapping<_Mapping>::value &&extents_type::rank() <= 1 && std::is_constructible_v)) MDSPAN_CONDITIONAL_EXPLICIT( (!std::is_convertible_v)) MDSPAN_INLINE_FUNCTION constexpr mapping(const _Mapping &other_mapping) noexcept : padded_stride(padded_stride_type::init_padding( other_mapping.extents(), other_mapping.extents().extent(extent_to_pad_idx))), exts(other_mapping.extents()) {} MDSPAN_INLINE_FUNCTION constexpr const extents_type & extents() const noexcept { return exts; } MDSPAN_INLINE_FUNCTION constexpr std::array strides() const noexcept { if constexpr (extents_type::rank() == 0) { return {}; } else if constexpr (extents_type::rank() == 1) { return {1}; } else { index_type value = 1; std::array s{}; s[extent_to_pad_idx] = value; value *= padded_stride.value(0); for (rank_type r = extent_to_pad_idx + 1; r < extents_type::rank() - 1; ++r) { s[r] = value; value *= exts.extent(r); } s[extents_type::rank() - 1] = value; return s; } } MDSPAN_INLINE_FUNCTION constexpr index_type required_span_size() const noexcept { if constexpr (extents_type::rank() == 0) { return 1; } else if constexpr (extents_type::rank() == 1) { return exts.extent(0); } else { index_type value = padded_stride.value(0); for (rank_type r = 1; r < extents_type::rank(); ++r) { value *= exts.extent(r); } return value; } } /** * Return the mapping given the provided indices per rank. * * This overload participates in overload resolution only if: * - `sizeof...(Indices) == extents_type::rank()`, * - `(is_convertible_v && ...) is true`, and * - (is_nothrow_constructible_v && ...) is true. */ MDSPAN_TEMPLATE_REQUIRES( class... _Indices, /* requires */ (sizeof...(_Indices) == extents_type::rank() && (::MDSPAN_IMPL_STANDARD_NAMESPACE::detail:: are_valid_indices()))) MDSPAN_INLINE_FUNCTION constexpr size_t operator()(_Indices... idxs) const noexcept { #if !defined(NDEBUG) ::MDSPAN_IMPL_STANDARD_NAMESPACE::detail::check_all_indices(this->extents(), idxs...); #endif // ! NDEBUG return compute_offset(std::index_sequence_for<_Indices...>{}, idxs...); } MDSPAN_INLINE_FUNCTION static constexpr bool is_always_unique() noexcept { return true; } MDSPAN_INLINE_FUNCTION static constexpr bool is_always_exhaustive() noexcept { return (extents_type::rank() <= rank_type(1)) || (extents_type::static_extent(extent_to_pad_idx) != dynamic_extent && extents_type::static_extent(extent_to_pad_idx) == padded_stride_type::static_value()); } MDSPAN_INLINE_FUNCTION static constexpr bool is_always_strided() noexcept { return true; } MDSPAN_INLINE_FUNCTION static constexpr bool is_unique() noexcept { return true; } MDSPAN_INLINE_FUNCTION constexpr bool is_exhaustive() const noexcept { return (extents_type::rank() < 2) || (exts.extent(extent_to_pad_idx) == padded_stride.value(0)); } MDSPAN_INLINE_FUNCTION static constexpr bool is_strided() noexcept { return true; } MDSPAN_INLINE_FUNCTION constexpr index_type stride(rank_type r) const noexcept { assert(r < extents_type::rank()); if (r == 0) return index_type(1); index_type value = padded_stride.value(0); for (rank_type k = 1; k < r; k++) value *= exts.extent(k); return value; } /** * Equality operator between `layout_left_padded`s * * This overload only participates in overload resolution if * `OtherExtents::rank() == extents_type::rank()`. * * \note There is currently a difference from p2642r2, where this function is * specified as taking `layout_left_padded< padding_value >::mapping< * Extents>`. However, this makes `padding_value` non-deducible. */ MDSPAN_TEMPLATE_REQUIRES( class _Mapping, /* requires */ (detail::is_layout_left_padded_mapping<_Mapping>::value && (_Mapping::extents_type::rank() == extents_type::rank()))) MDSPAN_INLINE_FUNCTION friend constexpr bool operator==(const mapping &left, const _Mapping &right) noexcept { // Workaround for some compilers not short-circuiting properly with // compile-time checks i.e. we can't access stride(_padding_stride_idx) of a // rank 0 mapping bool strides_equal = true; if constexpr (extents_type::rank() > rank_type(1)) { strides_equal = left.stride(padded_stride_idx) == right.stride(padded_stride_idx); } return (left.extents() == right.extents()) && strides_equal; } #if !MDSPAN_HAS_CXX_20 /** * Inequality operator between `layout_left_padded`s * * This overload only participates in overload resolution if * `OtherExtents::rank() == extents_type::rank()`. */ MDSPAN_TEMPLATE_REQUIRES( class _Mapping, /* requires */ (detail::is_layout_left_padded_mapping<_Mapping>::value && (_Mapping::extents_type::rank() == extents_type::rank()))) MDSPAN_INLINE_FUNCTION friend constexpr bool operator!=(const mapping &left, const _Mapping &right) noexcept { return !(left == right); } #endif // [mdspan.submdspan.mapping], submdspan mapping specialization template MDSPAN_INLINE_FUNCTION constexpr auto submdspan_mapping_impl( SliceSpecifiers... slices) const; template MDSPAN_INLINE_FUNCTION friend constexpr auto submdspan_mapping( const mapping& src, SliceSpecifiers... slices) { return src.submdspan_mapping_impl(slices...); } }; template template class layout_right_padded::mapping { public: static constexpr size_t padding_value = PaddingValue; using extents_type = Extents; using index_type = typename extents_type::index_type; using size_type = typename extents_type::size_type; using rank_type = typename extents_type::rank_type; using layout_type = layout_right_padded; #ifndef MDSPAN_INTERNAL_TEST private: #endif // MDSPAN_INTERNAL_TEST static constexpr rank_type padded_stride_idx = detail::layout_padded_constants::padded_stride_idx; static constexpr rank_type extent_to_pad_idx = detail::layout_padded_constants::extent_to_pad_idx; static_assert((padding_value != 0) || (extents_type::static_extent(extent_to_pad_idx) == 0) || (extents_type::static_extent(extent_to_pad_idx) == dynamic_extent), "if padding stride is 0, static_extent(extent-to-pad-rank) must also be 0 or dynamic_extent"); using padded_stride_type = detail::padded_extent< padding_value, extents_type, extent_to_pad_idx >; static constexpr size_t static_padding_stride = padded_stride_type::static_value(); typename padded_stride_type::static_array_type padded_stride = {}; extents_type exts = {}; MDSPAN_INLINE_FUNCTION constexpr index_type compute_offset(std::index_sequence<>) const { return 0; } template MDSPAN_INLINE_FUNCTION constexpr index_type compute_offset(std::index_sequence, IndexOffset index_offset) const { return index_offset; } template MDSPAN_INLINE_FUNCTION constexpr index_type compute_offset(std::index_sequence, IndexOffsets... index_offsets) const { // self-recursive fold trick from // https://github.com/llvm/llvm-project/blob/4d9771741d40cc9cfcccb6b033f43689d36b705a/libcxx/include/mdspan/layout_right.h#L141 index_type res = 0; ((res = static_cast(index_offsets) + (Ranks == extent_to_pad_idx ? padded_stride.value(0) : exts.extent(Ranks)) * res), ...); return res; } public: #if !MDSPAN_HAS_CXX_20 MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping() : mapping(extents_type{}) {} #else MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping() requires(static_padding_stride != dynamic_extent) = default; MDSPAN_INLINE_FUNCTION constexpr mapping() requires(static_padding_stride == dynamic_extent) : mapping(extents_type{}) {} #endif MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping(const mapping&) noexcept = default; MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping& operator=(const mapping&) noexcept = default; /** * Initializes the mapping with the given extents. * * \param ext the given extents */ MDSPAN_INLINE_FUNCTION constexpr mapping(const extents_type &ext) : padded_stride(padded_stride_type::init_padding(ext)), exts(ext) {} /** * Initializes the mapping with the given extents and the specified padding value. * * This overload participates in overload resolution only if `is_convertible_v` * is `true` and `is_nothrow_constructible_v` is `true` * * \param ext the given extents * \param padding_value the padding value */ MDSPAN_TEMPLATE_REQUIRES( class _Size, /* requires */ ( std::is_convertible_v<_Size, index_type> && std::is_nothrow_constructible_v ) ) MDSPAN_INLINE_FUNCTION constexpr mapping(const extents_type &ext, _Size dynamic_padding_value) : padded_stride(padded_stride_type::init_padding(ext, static_cast(dynamic_padding_value))), exts(ext) { assert((padding_value == dynamic_extent) || (static_cast(padding_value) == static_cast(dynamic_padding_value))); } /** * Converting constructor from `layout_right::mapping`. * * This overload participates in overload resolution only if `is_constructible_v` is true. * If `OtherExtents::rank() > 1` then one of `padding_value`, `static_extent(0)`, or `OtherExtents::static_extent(0)` must be `dynamic_extent`; * otherwise, `OtherExtents::static_extent(0)` must be equal to the least multiple of `padding_value` greater than or equal to `extents_type::static_extent(0)` */ MDSPAN_TEMPLATE_REQUIRES( class _OtherExtents, /* requires */ (std::is_constructible_v)) MDSPAN_CONDITIONAL_EXPLICIT( (!std::is_convertible_v<_OtherExtents, extents_type>)) MDSPAN_INLINE_FUNCTION constexpr mapping(const layout_right::mapping<_OtherExtents> &other_mapping) : padded_stride(padded_stride_type::init_padding( other_mapping, std::integral_constant{})), exts(other_mapping.extents()) { static_assert( (_OtherExtents::rank() > 1) || (padded_stride_type::static_value() != dynamic_extent) || (_OtherExtents::static_extent(extent_to_pad_idx) != dynamic_extent) || (padded_stride_type::static_value() == _OtherExtents::static_extent(extent_to_pad_idx))); } /** * Converting constructor from `layout_stride::mapping`. * * This overload participates in overload resolution only if * `is_constructible_v` is true */ MDSPAN_TEMPLATE_REQUIRES( class _OtherExtents, /* requires */ (std::is_constructible_v)) MDSPAN_CONDITIONAL_EXPLICIT((extents_type::rank() > 0)) MDSPAN_INLINE_FUNCTION constexpr mapping(const layout_stride::mapping<_OtherExtents> &other_mapping) : padded_stride(padded_stride_type::init_padding( other_mapping, std::integral_constant{})), exts(other_mapping.extents()) {} /** * Converting constructor from `layout_right_padded::mapping`. * * This overload participates in overload resolution only if * `is_constructible_v` is true. Either * `padding_value` or `OtherPaddingStride` must be `std::dynamic_extent`, or * `padding_value == OtherPaddingStride`. */ MDSPAN_TEMPLATE_REQUIRES( class _Mapping, /* requires */ (detail::is_layout_right_padded_mapping<_Mapping>::value &&std::is_constructible_v< extents_type, typename _Mapping::extents_type>)) MDSPAN_CONDITIONAL_EXPLICIT((extents_type::rank() > 1 && (padding_value == dynamic_extent || _Mapping::padding_value == dynamic_extent))) MDSPAN_INLINE_FUNCTION constexpr mapping(const _Mapping &other_mapping) : padded_stride(padded_stride_type::init_padding( other_mapping, std::integral_constant{})), exts(other_mapping.extents()) { static_assert(padding_value == dynamic_extent || _Mapping::padding_value == dynamic_extent || padding_value == _Mapping::padding_value); } /** * Converting constructor from `layout_left_padded::mapping`. * * This overload participates in overload resolution only if * `extents_type::rank()` is 0 or 1 and `is_constructible_v` is `true`. */ MDSPAN_TEMPLATE_REQUIRES( class _Mapping, /* requires */ (detail::is_layout_left_padded_mapping<_Mapping>::value &&extents_type::rank() <= 1 && std::is_constructible_v)) MDSPAN_CONDITIONAL_EXPLICIT( (!std::is_convertible_v)) MDSPAN_INLINE_FUNCTION constexpr mapping(const _Mapping &other_mapping) noexcept : padded_stride(padded_stride_type::init_padding( other_mapping.extents(), other_mapping.extents().extent(extent_to_pad_idx))), exts(other_mapping.extents()) {} MDSPAN_INLINE_FUNCTION constexpr const extents_type & extents() const noexcept { return exts; } MDSPAN_INLINE_FUNCTION constexpr std::array strides() const noexcept { if constexpr (extents_type::rank() == 0) { return {}; } else if constexpr (extents_type::rank() == 1) { return {1}; } else { index_type value = 1; std::array s{}; s[extent_to_pad_idx] = value; value *= padded_stride.value(0); for (rank_type r = extent_to_pad_idx - 1; r > 0; --r) { s[r] = value; value *= exts.extent(r); } s[0] = value; return s; } } MDSPAN_INLINE_FUNCTION constexpr index_type required_span_size() const noexcept { if constexpr (extents_type::rank() == 0) { return 1; } else if constexpr (extents_type::rank() == 1) { return exts.extent(0); } else { index_type value = 1; for (rank_type r = 0; r < extent_to_pad_idx; ++r) { value *= exts.extent(r); } return value * padded_stride.value(0); } } /** * Return the mapping given the provided indices per rank. * * This overload participates in overload resolution only if: * - `sizeof...(Indices) == extents_type::rank()`, * - `(is_convertible_v && ...) is true`, and * - (is_nothrow_constructible_v && ...) is true. */ MDSPAN_TEMPLATE_REQUIRES( class... _Indices, /* requires */ (sizeof...(_Indices) == extents_type::rank() && (::MDSPAN_IMPL_STANDARD_NAMESPACE::detail:: are_valid_indices()))) MDSPAN_INLINE_FUNCTION constexpr size_t operator()(_Indices... idxs) const noexcept { return compute_offset(std::index_sequence_for<_Indices...>{}, idxs...); } MDSPAN_INLINE_FUNCTION static constexpr bool is_always_unique() noexcept { return true; } MDSPAN_INLINE_FUNCTION static constexpr bool is_always_exhaustive() noexcept { return (extents_type::rank() <= rank_type(1)) || (extents_type::static_extent(extent_to_pad_idx) != dynamic_extent && extents_type::static_extent(extent_to_pad_idx) == padded_stride_type::static_value()); } MDSPAN_INLINE_FUNCTION static constexpr bool is_always_strided() noexcept { return true; } MDSPAN_INLINE_FUNCTION static constexpr bool is_unique() noexcept { return true; } MDSPAN_INLINE_FUNCTION constexpr bool is_exhaustive() const noexcept { return (extents_type::rank() < 2) || (exts.extent(extent_to_pad_idx) == padded_stride.value(0)); } MDSPAN_INLINE_FUNCTION static constexpr bool is_strided() noexcept { return true; } MDSPAN_INLINE_FUNCTION constexpr index_type stride(rank_type r) const noexcept { assert(r < extents_type::rank()); if (r == extents_type::rank() - 1) return index_type(1); index_type value = padded_stride.value(0); for (rank_type k = extents_type::rank() - 2; k > r; k--) value *= exts.extent(k); return value; } /** * Equality operator between `layout_right_padded`s * * This overload only participates in overload resolution if * `OtherExtents::rank() == extents_type::rank()`. * * \note There is currently a difference from p2642r2, where this function is * specified as taking `layout_right_padded< padding_value >::mapping< * Extents>`. However, this makes `padding_value` non-deducible. */ MDSPAN_TEMPLATE_REQUIRES( class _Mapping, /* requires */ (detail::is_layout_right_padded_mapping<_Mapping>::value && (_Mapping::extents_type::rank() == extents_type::rank()))) MDSPAN_INLINE_FUNCTION friend constexpr bool operator==(const mapping &left, const _Mapping &right) noexcept { // Workaround for some compilers not short-circuiting properly with // compile-time checks i.e. we can't access stride(_padding_stride_idx) of a // rank 0 mapping bool strides_equal = true; if constexpr (extents_type::rank() > rank_type(1)) { strides_equal = left.stride(padded_stride_idx) == right.stride(padded_stride_idx); } return (left.extents() == right.extents()) && strides_equal; } #if !MDSPAN_HAS_CXX_20 /** * Inequality operator between `layout_right_padded`s * * This overload only participates in overload resolution if * `OtherExtents::rank() == extents_type::rank()`. */ MDSPAN_TEMPLATE_REQUIRES( class _Mapping, /* requires */ (detail::is_layout_right_padded_mapping<_Mapping>::value && (_Mapping::extents_type::rank() == extents_type::rank()))) MDSPAN_INLINE_FUNCTION friend constexpr bool operator!=(const mapping &left, const _Mapping &right) noexcept { return !(left == right); } #endif // [mdspan.submdspan.mapping], submdspan mapping specialization template MDSPAN_INLINE_FUNCTION constexpr auto submdspan_mapping_impl( SliceSpecifiers... slices) const; template MDSPAN_INLINE_FUNCTION friend constexpr auto submdspan_mapping( const mapping& src, SliceSpecifiers... slices) { return src.submdspan_mapping_impl(slices...); } }; } } //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p2642_bits/layout_padded.hpp //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p2630_bits/submdspan.hpp //@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // See https://kokkos.org/LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p2630_bits/submdspan_extents.hpp //@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // See https://kokkos.org/LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER #include //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p2630_bits/strided_slice.hpp //@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // See https://kokkos.org/LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER #include namespace MDSPAN_IMPL_STANDARD_NAMESPACE { namespace { template struct __mdspan_is_integral_constant: std::false_type {}; template struct __mdspan_is_integral_constant>: std::true_type {}; } // Slice Specifier allowing for strides and compile time extent template struct strided_slice { using offset_type = OffsetType; using extent_type = ExtentType; using stride_type = StrideType; _MDSPAN_NO_UNIQUE_ADDRESS OffsetType offset{}; _MDSPAN_NO_UNIQUE_ADDRESS ExtentType extent{}; _MDSPAN_NO_UNIQUE_ADDRESS StrideType stride{}; static_assert(std::is_integral_v || __mdspan_is_integral_constant::value); static_assert(std::is_integral_v || __mdspan_is_integral_constant::value); static_assert(std::is_integral_v || __mdspan_is_integral_constant::value); }; } // MDSPAN_IMPL_STANDARD_NAMESPACE //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p2630_bits/strided_slice.hpp namespace MDSPAN_IMPL_STANDARD_NAMESPACE { namespace detail { // Mapping from submapping ranks to srcmapping ranks // InvMapRank is an index_sequence, which we build recursively // to contain the mapped indices. // end of recursion specialization containing the final index_sequence template MDSPAN_INLINE_FUNCTION constexpr auto inv_map_rank(std::integral_constant, std::index_sequence) { return std::index_sequence(); } // specialization reducing rank by one (i.e., integral slice specifier) template MDSPAN_INLINE_FUNCTION constexpr auto inv_map_rank(std::integral_constant, std::index_sequence, Slice, SliceSpecifiers... slices) { using next_idx_seq_t = std::conditional_t, std::index_sequence, std::index_sequence>; return inv_map_rank(std::integral_constant(), next_idx_seq_t(), slices...); } // Helper for identifying strided_slice template struct is_strided_slice : std::false_type {}; template struct is_strided_slice< strided_slice> : std::true_type {}; // first_of(slice): getting begin of slice specifier range MDSPAN_TEMPLATE_REQUIRES( class Integral, /* requires */(std::is_convertible_v) ) MDSPAN_INLINE_FUNCTION constexpr Integral first_of(const Integral &i) { return i; } MDSPAN_INLINE_FUNCTION constexpr std::integral_constant first_of(const ::MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent_t &) { return std::integral_constant(); } MDSPAN_TEMPLATE_REQUIRES( class Slice, /* requires */(std::is_convertible_v>) ) MDSPAN_INLINE_FUNCTION constexpr auto first_of(const Slice &i) { return std::get<0>(i); } template MDSPAN_INLINE_FUNCTION constexpr OffsetType first_of(const strided_slice &r) { return r.offset; } // last_of(slice): getting end of slice specifier range // We need however not just the slice but also the extents // of the original view and which rank from the extents. // This is needed in the case of slice being full_extent_t. MDSPAN_TEMPLATE_REQUIRES( size_t k, class Extents, class Integral, /* requires */(std::is_convertible_v) ) MDSPAN_INLINE_FUNCTION constexpr Integral last_of(std::integral_constant, const Extents &, const Integral &i) { return i; } MDSPAN_TEMPLATE_REQUIRES( size_t k, class Extents, class Slice, /* requires */(std::is_convertible_v>) ) MDSPAN_INLINE_FUNCTION constexpr auto last_of(std::integral_constant, const Extents &, const Slice &i) { return std::get<1>(i); } // Suppress spurious warning with NVCC about no return statement. // This is a known issue in NVCC and NVC++ // Depending on the CUDA and GCC version we need both the builtin // and the diagnostic push. I tried really hard to find something shorter // but no luck ... #if defined __NVCC__ #ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ #pragma nv_diagnostic push #pragma nv_diag_suppress = implicit_return_from_non_void_function #else #ifdef __CUDA_ARCH__ #pragma diagnostic push #pragma diag_suppress implicit_return_from_non_void_function #endif #endif #elif defined __NVCOMPILER #pragma diagnostic push #pragma diag_suppress = implicit_return_from_non_void_function #endif template MDSPAN_INLINE_FUNCTION constexpr auto last_of(std::integral_constant, const Extents &ext, ::MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent_t) { if constexpr (Extents::static_extent(k) == dynamic_extent) { return ext.extent(k); } else { return std::integral_constant(); } #if defined(__NVCC__) && !defined(__CUDA_ARCH__) && defined(__GNUC__) // Even with CUDA_ARCH protection this thing warns about calling host function __builtin_unreachable(); #endif } #if defined __NVCC__ #ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ #pragma nv_diagnostic pop #else #ifdef __CUDA_ARCH__ #pragma diagnostic pop #endif #endif #elif defined __NVCOMPILER #pragma diagnostic pop #endif template MDSPAN_INLINE_FUNCTION constexpr OffsetType last_of(std::integral_constant, const Extents &, const strided_slice &r) { return r.extent; } // get stride of slices template MDSPAN_INLINE_FUNCTION constexpr auto stride_of(const T &) { return std::integral_constant(); } template MDSPAN_INLINE_FUNCTION constexpr auto stride_of(const strided_slice &r) { return r.stride; } // divide which can deal with integral constant preservation template MDSPAN_INLINE_FUNCTION constexpr auto divide(const T0 &v0, const T1 &v1) { return IndexT(v0) / IndexT(v1); } template MDSPAN_INLINE_FUNCTION constexpr auto divide(const std::integral_constant &, const std::integral_constant &) { // cutting short division by zero // this is used for strided_slice with zero extent/stride return std::integral_constant(); } // multiply which can deal with integral constant preservation template MDSPAN_INLINE_FUNCTION constexpr auto multiply(const T0 &v0, const T1 &v1) { return IndexT(v0) * IndexT(v1); } template MDSPAN_INLINE_FUNCTION constexpr auto multiply(const std::integral_constant &, const std::integral_constant &) { return std::integral_constant(); } // compute new static extent from range, preserving static knowledge template struct StaticExtentFromRange { constexpr static size_t value = dynamic_extent; }; template struct StaticExtentFromRange, std::integral_constant> { constexpr static size_t value = val1 - val0; }; // compute new static extent from strided_slice, preserving static // knowledge template struct StaticExtentFromStridedRange { constexpr static size_t value = dynamic_extent; }; template struct StaticExtentFromStridedRange, std::integral_constant> { constexpr static size_t value = val0 > 0 ? 1 + (val0 - 1) / val1 : 0; }; // creates new extents through recursive calls to next_extent member function // next_extent has different overloads for different types of stride specifiers template struct extents_constructor { MDSPAN_TEMPLATE_REQUIRES( class Slice, class... SlicesAndExtents, /* requires */(!std::is_convertible_v && !is_strided_slice::value) ) MDSPAN_INLINE_FUNCTION constexpr static auto next_extent(const Extents &ext, const Slice &sl, SlicesAndExtents... slices_and_extents) { constexpr size_t new_static_extent = StaticExtentFromRange< decltype(first_of(std::declval())), decltype(last_of(std::integral_constant(), std::declval(), std::declval()))>::value; using next_t = extents_constructor; using index_t = typename Extents::index_type; return next_t::next_extent( ext, slices_and_extents..., index_t(last_of(std::integral_constant(), ext, sl)) - index_t(first_of(sl))); } MDSPAN_TEMPLATE_REQUIRES( class Slice, class... SlicesAndExtents, /* requires */ (std::is_convertible_v) ) MDSPAN_INLINE_FUNCTION constexpr static auto next_extent(const Extents &ext, const Slice &, SlicesAndExtents... slices_and_extents) { using next_t = extents_constructor; return next_t::next_extent(ext, slices_and_extents...); } template MDSPAN_INLINE_FUNCTION constexpr static auto next_extent(const Extents &ext, const strided_slice &r, SlicesAndExtents... slices_and_extents) { using index_t = typename Extents::index_type; using new_static_extent_t = StaticExtentFromStridedRange; if constexpr (new_static_extent_t::value == dynamic_extent) { using next_t = extents_constructor; return next_t::next_extent( ext, slices_and_extents..., r.extent > 0 ? 1 + divide(r.extent - 1, r.stride) : 0); } else { constexpr size_t new_static_extent = new_static_extent_t::value; using next_t = extents_constructor; return next_t::next_extent( ext, slices_and_extents..., index_t(divide(ExtentType(), StrideType()))); } } }; template struct extents_constructor<0, Extents, NewStaticExtents...> { template MDSPAN_INLINE_FUNCTION constexpr static auto next_extent(const Extents &, NewExtents... new_exts) { return extents( new_exts...); } }; } // namespace detail // submdspan_extents creates new extents given src extents and submdspan slice // specifiers template MDSPAN_INLINE_FUNCTION constexpr auto submdspan_extents(const extents &src_exts, SliceSpecifiers... slices) { using ext_t = extents; return detail::extents_constructor::next_extent( src_exts, slices...); } } // namespace MDSPAN_IMPL_STANDARD_NAMESPACE //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p2630_bits/submdspan_extents.hpp //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p2630_bits/submdspan_mapping.hpp //@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // See https://kokkos.org/LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER #include #include #include #include // index_sequence // Suppress spurious warning with NVCC about no return statement. // This is a known issue in NVCC and NVC++ // Depending on the CUDA and GCC version we need both the builtin // and the diagnostic push. I tried really hard to find something shorter // but no luck ... #if defined __NVCC__ #ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ #pragma nv_diagnostic push #pragma nv_diag_suppress = implicit_return_from_non_void_function #else #ifdef __CUDA_ARCH__ #pragma diagnostic push #pragma diag_suppress implicit_return_from_non_void_function #endif #endif #elif defined __NVCOMPILER #pragma diagnostic push #pragma diag_suppress = implicit_return_from_non_void_function #endif namespace MDSPAN_IMPL_STANDARD_NAMESPACE { //****************************************** // Return type of submdspan_mapping overloads //****************************************** template struct submdspan_mapping_result { _MDSPAN_NO_UNIQUE_ADDRESS LayoutMapping mapping{}; size_t offset; }; namespace detail { // We use const Slice& and not Slice&& because the various // submdspan_mapping_impl overloads use their slices arguments // multiple times. This makes perfect forwarding not useful, but we // still don't want to pass those (possibly of size 64 x 3 bits) // objects by value. template MDSPAN_INLINE_FUNCTION constexpr bool one_slice_out_of_bounds(const IndexType &ext, const Slice &slice) { using common_t = std::common_type_t; return static_cast(detail::first_of(slice)) == static_cast(ext); } template MDSPAN_INLINE_FUNCTION constexpr bool any_slice_out_of_bounds_helper(std::index_sequence, const extents &exts, const Slices &... slices) { return _MDSPAN_FOLD_OR( (one_slice_out_of_bounds(exts.extent(RankIndices), slices))); } template MDSPAN_INLINE_FUNCTION constexpr bool any_slice_out_of_bounds(const extents &exts, const Slices &... slices) { return any_slice_out_of_bounds_helper( std::make_index_sequence(), exts, slices...); } // constructs sub strides template MDSPAN_INLINE_FUNCTION constexpr auto construct_sub_strides( const SrcMapping &src_mapping, std::index_sequence, const std::tuple &slices_stride_factor) { using index_type = typename SrcMapping::index_type; return std::array{ (static_cast(src_mapping.stride(InvMapIdxs)) * static_cast(std::get(slices_stride_factor)))...}; } template struct is_range_slice { constexpr static bool value = std::is_same_v || std::is_convertible_v>; }; template constexpr bool is_range_slice_v = is_range_slice::value; template struct is_index_slice { constexpr static bool value = std::is_convertible_v; }; template constexpr bool is_index_slice_v = is_index_slice::value; } // namespace detail //********************************** // layout_left submdspan_mapping //********************************* namespace detail { // Figure out whether to preserve layout_left template struct deduce_layout_left_submapping; template struct deduce_layout_left_submapping< IndexType, SubRank, std::index_sequence, SliceSpecifiers...> { using count_range = index_sequence_scan_impl< 0, (is_index_slice_v ? 0 : 1)...>; constexpr static int gap_len = (((Idx > 0 && count_range::get(Idx) == 1 && is_index_slice_v) ? 1 : 0) + ... + 0); MDSPAN_INLINE_FUNCTION constexpr static bool layout_left_value() { // Use layout_left for rank 0 if constexpr (SubRank == 0) { return true; // Use layout_left for rank 1 result if leftmost slice specifier is range like } else if constexpr (SubRank == 1) { return ((Idx > 0 || is_range_slice_v)&&...); } else { // Preserve if leftmost SubRank-1 slices are full_extent_t and // the slice at idx Subrank - 1 is a range and // for idx > SubRank the slice is an index return ((((Idx < SubRank - 1) && std::is_same_v) || ((Idx == SubRank - 1) && is_range_slice_v) || ((Idx > SubRank - 1) && is_index_slice_v)) && ...); } #if defined(__NVCC__) && !defined(__CUDA_ARCH__) && defined(__GNUC__) __builtin_unreachable(); #endif } MDSPAN_INLINE_FUNCTION constexpr static bool layout_left_padded_value() { // Technically could also keep layout_left_padded for SubRank==0 // and SubRank==1 with leftmost slice specifier being a contiguous range // but we intercept these cases separately // In all other cases: // leftmost slice must be range // then there can be a gap with index slices // then SubRank - 2 full_extent slices // then another range slice // then more index slices // e.g. R I I I F F F R I I for obtaining a rank-5 from a rank-10 return ((((Idx == 0) && is_range_slice_v) || ((Idx > 0 && Idx <= gap_len) && is_index_slice_v) || ((Idx > gap_len && Idx < gap_len + SubRank - 1) && std::is_same_v) || ((Idx == gap_len + SubRank - 1) && is_range_slice_v) || ((Idx > gap_len + SubRank - 1) && is_index_slice_v)) && ... ); } }; // We are reusing the same thing for layout_left and layout_left_padded // For layout_left as source StaticStride is static_extent(0) template struct compute_s_static_layout_left { // Neither StaticStride nor any of the provided extents can be zero. // StaticStride can never be zero, the static_extents we are looking at are associated with // integral slice specifiers - which wouldn't be valid for zero extent template MDSPAN_INLINE_FUNCTION static constexpr size_t value(std::index_sequence) { size_t val = ((Idx>0 && Idx<=NumGaps ? (Extents::static_extent(Idx) == dynamic_extent?0:Extents::static_extent(Idx)) : 1) * ... * (StaticStride == dynamic_extent?0:StaticStride)); return val == 0?dynamic_extent:val; } }; } // namespace detail // Actual submdspan mapping call template template MDSPAN_INLINE_FUNCTION constexpr auto layout_left::mapping::submdspan_mapping_impl( SliceSpecifiers... slices) const { // compute sub extents using src_ext_t = Extents; auto dst_ext = submdspan_extents(extents(), slices...); using dst_ext_t = decltype(dst_ext); // figure out sub layout type using deduce_layout = detail::deduce_layout_left_submapping< typename dst_ext_t::index_type, dst_ext_t::rank(), std::make_index_sequence, SliceSpecifiers...>; // Figure out if any slice's lower bound equals the corresponding extent. // If so, bypass evaluating the layout mapping. This fixes LWG Issue 4060. const bool out_of_bounds = detail::any_slice_out_of_bounds(this->extents(), slices...); auto offset = static_cast( out_of_bounds ? this->required_span_size() : this->operator()(detail::first_of(slices)...)); if constexpr (deduce_layout::layout_left_value()) { // layout_left case using dst_mapping_t = typename layout_left::template mapping; return submdspan_mapping_result{dst_mapping_t(dst_ext), offset}; } else if constexpr (deduce_layout::layout_left_padded_value()) { constexpr size_t S_static = MDSPAN_IMPL_STANDARD_NAMESPACE::detail::compute_s_static_layout_left::value(std::make_index_sequence()); using dst_mapping_t = typename MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_left_padded::template mapping; return submdspan_mapping_result{ dst_mapping_t(dst_ext, stride(1 + deduce_layout::gap_len)), offset}; } else { // layout_stride case using dst_mapping_t = typename layout_stride::mapping; auto inv_map = detail::inv_map_rank(std::integral_constant(), std::index_sequence<>(), slices...); return submdspan_mapping_result { dst_mapping_t(dst_ext, detail::construct_sub_strides( *this, inv_map, // HIP needs deduction guides to have markups so we need to be explicit // NVCC 11.0 has a bug with deduction guide here, tested that 11.2 does not have // the issue But Clang-CUDA also doesn't accept the use of deduction guide so // disable it for CUDA altogether #if defined(_MDSPAN_HAS_HIP) || defined(_MDSPAN_HAS_CUDA) std::tuple{ detail::stride_of(slices)...})), #else std::tuple{detail::stride_of(slices)...})), #endif offset }; } #if defined(__NVCC__) && !defined(__CUDA_ARCH__) && defined(__GNUC__) __builtin_unreachable(); #endif } template template template MDSPAN_INLINE_FUNCTION constexpr auto MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_left_padded::mapping::submdspan_mapping_impl( SliceSpecifiers... slices) const { // compute sub extents using src_ext_t = Extents; auto dst_ext = submdspan_extents(extents(), slices...); using dst_ext_t = decltype(dst_ext); if constexpr (Extents::rank() == 0) { // rank-0 case using dst_mapping_t = typename MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_left_padded::template mapping; return submdspan_mapping_result{*this, 0}; } else { const bool out_of_bounds = MDSPAN_IMPL_STANDARD_NAMESPACE::detail::any_slice_out_of_bounds(this->extents(), slices...); auto offset = static_cast( out_of_bounds ? this->required_span_size() : this->operator()(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::first_of(slices)...)); if constexpr (dst_ext_t::rank() == 0) { // result rank-0 // The following for some reasons leads to compiler error later, while not using a typedef works: // Compilers: CUDA 11.2 with GCC 9.1 // // using dst_mapping_t = typename layout_left::template mapping; // return submdspan_mapping_result{dst_mapping_t{dst_ext}, offset}; // // Error: submdspan_mapping.hpp:299:23: error: 'dst_mapping_t' does not name a type // 299 | using dst_mapping_t = typename layout_left::template mapping; // The same error is given (about dst_mapping_t not naming type) when a different name is used in 299: // using dst_mapping_t2 = typename layout_left::template mapping; return submdspan_mapping_result> {typename layout_left::template mapping{dst_ext}, offset}; } else { // general case // Figure out if any slice's lower bound equals the corresponding extent. // If so, bypass evaluating the layout mapping. This fixes LWG Issue 4060. // figure out sub layout type using deduce_layout = MDSPAN_IMPL_STANDARD_NAMESPACE::detail::deduce_layout_left_submapping< typename dst_ext_t::index_type, dst_ext_t::rank(), decltype(std::make_index_sequence()), SliceSpecifiers...>; if constexpr (deduce_layout::layout_left_value() && dst_ext_t::rank() == 1) { // getting rank-1 from leftmost using dst_mapping_t = typename layout_left::template mapping; return submdspan_mapping_result{dst_mapping_t{dst_ext}, offset}; } else if constexpr (deduce_layout::layout_left_padded_value()) { // can keep layout_left_padded constexpr size_t S_static = MDSPAN_IMPL_STANDARD_NAMESPACE::detail::compute_s_static_layout_left::value(std::make_index_sequence()); using dst_mapping_t = typename MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_left_padded::template mapping; return submdspan_mapping_result{ dst_mapping_t(dst_ext, stride(1 + deduce_layout::gap_len)), offset}; } else { // layout_stride auto inv_map = MDSPAN_IMPL_STANDARD_NAMESPACE::detail::inv_map_rank(std::integral_constant(), std::index_sequence<>(), slices...); using dst_mapping_t = typename layout_stride::template mapping; return submdspan_mapping_result { dst_mapping_t(dst_ext, MDSPAN_IMPL_STANDARD_NAMESPACE::detail::construct_sub_strides( *this, inv_map, // HIP needs deduction guides to have markups so we need to be explicit // NVCC 11.0 has a bug with deduction guide here, tested that 11.2 does not have // the issue But Clang-CUDA also doesn't accept the use of deduction guide so // disable it for CUDA alltogether #if defined(_MDSPAN_HAS_HIP) || defined(_MDSPAN_HAS_CUDA) std::tuple{ MDSPAN_IMPL_STANDARD_NAMESPACE::detail::stride_of(slices)...})), #else std::tuple{MDSPAN_IMPL_STANDARD_NAMESPACE::detail::stride_of(slices)...})), #endif offset }; } } } #if defined(__NVCC__) && !defined(__CUDA_ARCH__) && defined(__GNUC__) __builtin_unreachable(); #endif } //********************************** // layout_right submdspan_mapping //********************************* namespace detail { // Figure out whether to preserve layout_right template struct deduce_layout_right_submapping; template struct deduce_layout_right_submapping< IndexType, SubRank, std::index_sequence, SliceSpecifiers...> { static constexpr size_t Rank = sizeof...(Idx); using count_range = index_sequence_scan_impl< 0, (std::is_convertible_v ? 0 : 1)...>; //__static_partial_sums...>; constexpr static int gap_len = (((Idx < Rank - 1 && count_range::get(Idx) == SubRank - 1 && std::is_convertible_v) ? 1 : 0) + ... + 0); MDSPAN_INLINE_FUNCTION constexpr static bool layout_right_value() { // Use layout_right for rank 0 if constexpr (SubRank == 0) { return true; // Use layout_right for rank 1 result if rightmost slice specifier is range like } else if constexpr (SubRank == 1) { return ((Idx < Rank - 1 || is_range_slice_v)&&...); } else { // Preserve if rightmost SubRank-1 slices are full_extent_t and // the slice at idx Rank-Subrank is a range and // for idx < Rank - SubRank the slice is an index return ((((Idx >= Rank - SubRank) && std::is_same_v) || ((Idx == Rank - SubRank) && is_range_slice_v) || ((Idx < Rank - SubRank) && is_index_slice_v)) && ...); } #if defined(__NVCC__) && !defined(__CUDA_ARCH__) && defined(__GNUC__) __builtin_unreachable(); #endif } MDSPAN_INLINE_FUNCTION constexpr static bool layout_right_padded_value() { // Technically could also keep layout_right_padded for SubRank==0 // and SubRank==1 with rightmost slice specifier being a contiguous range // but we intercept these cases separately // In all other cases: // rightmost slice must be range // then there can be a gap with index slices // then SubRank - 2 full_extent slices // then another range slice // then more index slices // e.g. I I R F F F I I I R for obtaining a rank-5 from a rank-10 return ((((Idx == Rank - 1) && is_range_slice_v) || ((Idx >= Rank - gap_len - 1 && Idx < Rank - 1) && is_index_slice_v) || ((Idx > Rank - gap_len - SubRank && Idx < Rank - gap_len - 1) && std::is_same_v) || ((Idx == Rank - gap_len - SubRank) && is_range_slice_v) || ((Idx < Rank - gap_len - SubRank) && is_index_slice_v)) && ... ); } }; // We are reusing the same thing for layout_right and layout_right_padded // For layout_right as source StaticStride is static_extent(Rank-1) template struct compute_s_static_layout_right { // Neither StaticStride nor any of the provided extents can be zero. // StaticStride can never be zero, the static_extents we are looking at are associated with // integral slice specifiers - which wouldn't be valid for zero extent template MDSPAN_INLINE_FUNCTION static constexpr size_t value(std::index_sequence) { size_t val = ((Idx >= Extents::rank() - 1 - NumGaps && Idx < Extents::rank() - 1 ? (Extents::static_extent(Idx) == dynamic_extent?0:Extents::static_extent(Idx)) : 1) * ... * (StaticStride == dynamic_extent?0:StaticStride)); return val == 0?dynamic_extent:val; } }; } // namespace detail // Actual submdspan mapping call template template MDSPAN_INLINE_FUNCTION constexpr auto layout_right::mapping::submdspan_mapping_impl( SliceSpecifiers... slices) const { // compute sub extents using src_ext_t = Extents; auto dst_ext = submdspan_extents(extents(), slices...); using dst_ext_t = decltype(dst_ext); // figure out sub layout type using deduce_layout = detail::deduce_layout_right_submapping< typename dst_ext_t::index_type, dst_ext_t::rank(), std::make_index_sequence, SliceSpecifiers...>; // Figure out if any slice's lower bound equals the corresponding extent. // If so, bypass evaluating the layout mapping. This fixes LWG Issue 4060. const bool out_of_bounds = detail::any_slice_out_of_bounds(this->extents(), slices...); auto offset = static_cast( out_of_bounds ? this->required_span_size() : this->operator()(detail::first_of(slices)...)); if constexpr (deduce_layout::layout_right_value()) { // layout_right case using dst_mapping_t = typename layout_right::mapping; return submdspan_mapping_result{dst_mapping_t(dst_ext), offset}; } else if constexpr (deduce_layout::layout_right_padded_value()) { constexpr size_t S_static = MDSPAN_IMPL_STANDARD_NAMESPACE::detail::compute_s_static_layout_left::value(std::make_index_sequence()); using dst_mapping_t = typename MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_right_padded::template mapping; return submdspan_mapping_result{ dst_mapping_t(dst_ext, stride(src_ext_t::rank() - 2 - deduce_layout::gap_len)), offset}; } else { // layout_stride case using dst_mapping_t = typename layout_stride::mapping; auto inv_map = detail::inv_map_rank(std::integral_constant(), std::index_sequence<>(), slices...); return submdspan_mapping_result { dst_mapping_t(dst_ext, detail::construct_sub_strides( *this, inv_map, // HIP needs deduction guides to have markups so we need to be explicit // NVCC 11.0 has a bug with deduction guide here, tested that 11.2 does not have // the issue But Clang-CUDA also doesn't accept the use of deduction guide so // disable it for CUDA altogether #if defined(_MDSPAN_HAS_HIP) || defined(_MDSPAN_HAS_CUDA) std::tuple{ detail::stride_of(slices)...})), #else std::tuple{detail::stride_of(slices)...})), #endif offset }; } #if defined(__NVCC__) && !defined(__CUDA_ARCH__) && defined(__GNUC__) __builtin_unreachable(); #endif } template template template MDSPAN_INLINE_FUNCTION constexpr auto MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_right_padded::mapping::submdspan_mapping_impl( SliceSpecifiers... slices) const { // compute sub extents using src_ext_t = Extents; auto dst_ext = submdspan_extents(extents(), slices...); using dst_ext_t = decltype(dst_ext); if constexpr (Extents::rank() == 0) { // rank-0 case using dst_mapping_t = typename MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_right_padded::template mapping; return submdspan_mapping_result{*this, 0}; } else { // Figure out if any slice's lower bound equals the corresponding extent. // If so, bypass evaluating the layout mapping. This fixes LWG Issue 4060. // figure out sub layout type const bool out_of_bounds = MDSPAN_IMPL_STANDARD_NAMESPACE::detail::any_slice_out_of_bounds(this->extents(), slices...); auto offset = static_cast( out_of_bounds ? this->required_span_size() : this->operator()(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::first_of(slices)...)); if constexpr (dst_ext_t::rank() == 0) { // result rank-0 // Same issue as in layout_left_padded: see comment there // using dst_mapping_t = typename layout_right::template mapping; // return submdspan_mapping_result{dst_mapping_t{dst_ext}, offset}; return submdspan_mapping_result> {typename layout_right::template mapping{dst_ext}, offset}; } else { // general case using deduce_layout = MDSPAN_IMPL_STANDARD_NAMESPACE::detail::deduce_layout_right_submapping< typename dst_ext_t::index_type, dst_ext_t::rank(), decltype(std::make_index_sequence()), SliceSpecifiers...>; if constexpr (deduce_layout::layout_right_value() && dst_ext_t::rank() == 1) { // getting rank-1 from rightmost using dst_mapping_t = typename layout_right::template mapping; return submdspan_mapping_result{dst_mapping_t{dst_ext}, offset}; } else if constexpr (deduce_layout::layout_right_padded_value()) { // can keep layout_right_padded constexpr size_t S_static = MDSPAN_IMPL_STANDARD_NAMESPACE::detail::compute_s_static_layout_right::value(std::make_index_sequence()); using dst_mapping_t = typename MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_right_padded::template mapping; return submdspan_mapping_result{ dst_mapping_t(dst_ext, stride(Extents::rank() - 2 - deduce_layout::gap_len)), offset}; } else { // layout_stride auto inv_map = MDSPAN_IMPL_STANDARD_NAMESPACE::detail::inv_map_rank(std::integral_constant(), std::index_sequence<>(), slices...); using dst_mapping_t = typename layout_stride::template mapping; return submdspan_mapping_result { dst_mapping_t(dst_ext, MDSPAN_IMPL_STANDARD_NAMESPACE::detail::construct_sub_strides( *this, inv_map, // HIP needs deduction guides to have markups so we need to be explicit // NVCC 11.0 has a bug with deduction guide here, tested that 11.2 does not have // the issue But Clang-CUDA also doesn't accept the use of deduction guide so // disable it for CUDA alltogether #if defined(_MDSPAN_HAS_HIP) || defined(_MDSPAN_HAS_CUDA) std::tuple{ MDSPAN_IMPL_STANDARD_NAMESPACE::detail::stride_of(slices)...})), #else std::tuple{MDSPAN_IMPL_STANDARD_NAMESPACE::detail::stride_of(slices)...})), #endif offset }; } } } #if defined(__NVCC__) && !defined(__CUDA_ARCH__) && defined(__GNUC__) __builtin_unreachable(); #endif } //********************************** // layout_stride submdspan_mapping //********************************* template template MDSPAN_INLINE_FUNCTION constexpr auto layout_stride::mapping::submdspan_mapping_impl( SliceSpecifiers... slices) const { auto dst_ext = submdspan_extents(extents(), slices...); using dst_ext_t = decltype(dst_ext); auto inv_map = detail::inv_map_rank(std::integral_constant(), std::index_sequence<>(), slices...); using dst_mapping_t = typename layout_stride::template mapping; // Figure out if any slice's lower bound equals the corresponding extent. // If so, bypass evaluating the layout mapping. This fixes LWG Issue 4060. const bool out_of_bounds = detail::any_slice_out_of_bounds(this->extents(), slices...); auto offset = static_cast( out_of_bounds ? this->required_span_size() : this->operator()(detail::first_of(slices)...)); return submdspan_mapping_result { dst_mapping_t(dst_ext, detail::construct_sub_strides( *this, inv_map, // HIP needs deduction guides to have markups so we need to be explicit // NVCC 11.0 has a bug with deduction guide here, tested that 11.2 does not have // the issue #if defined(_MDSPAN_HAS_HIP) || \ (defined(__NVCC__) && \ (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__ * 10) < 1120) std::tuple( detail::stride_of(slices)...))), #else std::tuple(detail::stride_of(slices)...))), #endif offset }; } } // namespace MDSPAN_IMPL_STANDARD_NAMESPACE #if defined __NVCC__ #ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ #pragma nv_diagnostic pop #else #ifdef __CUDA_ARCH__ #pragma diagnostic pop #endif #endif #elif defined __NVCOMPILER #pragma diagnostic pop #endif //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p2630_bits/submdspan_mapping.hpp namespace MDSPAN_IMPL_STANDARD_NAMESPACE { template MDSPAN_INLINE_FUNCTION constexpr auto submdspan(const mdspan &src, SliceSpecifiers... slices) { const auto sub_submdspan_mapping_result = submdspan_mapping(src.mapping(), slices...); // NVCC has a problem with the deduction so lets figure out the type using sub_mapping_t = std::remove_cv_t; using sub_extents_t = typename sub_mapping_t::extents_type; using sub_layout_t = typename sub_mapping_t::layout_type; using sub_accessor_t = typename AccessorPolicy::offset_policy; return mdspan( src.accessor().offset(src.data_handle(), sub_submdspan_mapping_result.offset), sub_submdspan_mapping_result.mapping, sub_accessor_t(src.accessor())); } } // namespace MDSPAN_IMPL_STANDARD_NAMESPACE //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p2630_bits/submdspan.hpp #endif //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p2389_bits/dims.hpp //@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // See https://kokkos.org/LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER // backward compatibility import into experimental namespace MDSPAN_IMPL_STANDARD_NAMESPACE { namespace MDSPAN_IMPL_PROPOSED_NAMESPACE { template< ::std::size_t Rank, class IndexType = std::size_t> using dims = :: MDSPAN_IMPL_STANDARD_NAMESPACE :: dextents; } // namespace MDSPAN_IMPL_PROPOSED_NAMESPACE } // namespace MDSPAN_IMPL_STANDARD_NAMESPACE //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p2389_bits/dims.hpp #endif // MDSPAN_HPP_ //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/mdspan/mdspan.hpp // backward compatibility import into experimental namespace MDSPAN_IMPL_STANDARD_NAMESPACE { namespace MDSPAN_IMPL_PROPOSED_NAMESPACE { using ::MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan; using ::MDSPAN_IMPL_STANDARD_NAMESPACE::extents; using ::MDSPAN_IMPL_STANDARD_NAMESPACE::layout_left; using ::MDSPAN_IMPL_STANDARD_NAMESPACE::layout_right; using ::MDSPAN_IMPL_STANDARD_NAMESPACE::layout_stride; using ::MDSPAN_IMPL_STANDARD_NAMESPACE::default_accessor; } } //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/mdspan //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/mdspan/mdarray.hpp //@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // See https://kokkos.org/LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER #ifndef MDARRAY_HPP_ #define MDARRAY_HPP_ #ifndef MDSPAN_IMPL_STANDARD_NAMESPACE #define MDSPAN_IMPL_STANDARD_NAMESPACE Kokkos #endif #ifndef MDSPAN_IMPL_PROPOSED_NAMESPACE #define MDSPAN_IMPL_PROPOSED_NAMESPACE Experimental #endif //BEGIN_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p1684_bits/mdarray.hpp //@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // See https://kokkos.org/LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER #include #include namespace MDSPAN_IMPL_STANDARD_NAMESPACE { namespace MDSPAN_IMPL_PROPOSED_NAMESPACE { namespace { template struct size_of_extents; template struct size_of_extents> { constexpr static size_t value() { size_t size = 1; for(size_t r=0; r::rank(); r++) size *= extents::static_extent(r); return size; } }; } namespace { template struct container_is_array : std::false_type { template static constexpr C construct(const M& m) { return C(m.required_span_size()); } }; template struct container_is_array> : std::true_type { template static constexpr std::array construct(const M&) { return std::array(); } }; } template < class ElementType, class Extents, class LayoutPolicy = layout_right, class Container = std::vector > class mdarray { private: static_assert(::MDSPAN_IMPL_STANDARD_NAMESPACE::detail::__is_extents_v, MDSPAN_IMPL_PROPOSED_NAMESPACE_STRING "::mdspan's Extents template parameter must be a specialization of " MDSPAN_IMPL_STANDARD_NAMESPACE_STRING "::extents."); public: //-------------------------------------------------------------------------------- // Domain and codomain types using extents_type = Extents; using layout_type = LayoutPolicy; using container_type = Container; using mapping_type = typename layout_type::template mapping; using element_type = ElementType; using mdspan_type = mdspan; using const_mdspan_type = mdspan; using value_type = std::remove_cv_t; using index_type = typename Extents::index_type; using size_type = typename Extents::size_type; using rank_type = typename Extents::rank_type; using pointer = typename container_type::pointer; using reference = typename container_type::reference; using const_pointer = typename container_type::const_pointer; using const_reference = typename container_type::const_reference; public: //-------------------------------------------------------------------------------- // [mdspan.basic.cons], mdspan constructors, assignment, and destructor #if !(MDSPAN_HAS_CXX_20) MDSPAN_FUNCTION_REQUIRES( (MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr), mdarray, (), , /* requires */ (extents_type::rank_dynamic()!=0)) {} #else MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mdarray() requires(extents_type::rank_dynamic()!=0) = default; #endif MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mdarray(const mdarray&) = default; MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mdarray(mdarray&&) = default; // Constructors for container types constructible from a size MDSPAN_TEMPLATE_REQUIRES( class... SizeTypes, /* requires */ ( (::MDSPAN_IMPL_STANDARD_NAMESPACE::detail::are_valid_indices()) && _MDSPAN_TRAIT( std::is_constructible, extents_type, SizeTypes...) && _MDSPAN_TRAIT( std::is_constructible, mapping_type, extents_type) && (_MDSPAN_TRAIT( std::is_constructible, container_type, size_t) || container_is_array::value) && (extents_type::rank()>0 || extents_type::rank_dynamic()==0) ) ) MDSPAN_INLINE_FUNCTION explicit constexpr mdarray(SizeTypes... dynamic_extents) : map_(extents_type(dynamic_extents...)), ctr_(container_is_array::construct(map_)) { } MDSPAN_FUNCTION_REQUIRES( (MDSPAN_INLINE_FUNCTION constexpr), mdarray, (const extents_type& exts), , /* requires */ ((_MDSPAN_TRAIT( std::is_constructible, container_type, size_t) || container_is_array::value) && _MDSPAN_TRAIT( std::is_constructible, mapping_type, extents_type)) ) : map_(exts), ctr_(container_is_array::construct(map_)) { } MDSPAN_FUNCTION_REQUIRES( (MDSPAN_INLINE_FUNCTION constexpr), mdarray, (const mapping_type& m), , /* requires */ (_MDSPAN_TRAIT( std::is_constructible, container_type, size_t) || container_is_array::value) ) : map_(m), ctr_(container_is_array::construct(map_)) { } MDSPAN_FUNCTION_REQUIRES( (MDSPAN_INLINE_FUNCTION constexpr), mdarray, (const extents_type& exts, const container_type& ctr), , /* requires */ (_MDSPAN_TRAIT( std::is_constructible, mapping_type, extents_type)) ) : map_(exts), ctr_(ctr) { assert(ctr.size() >= static_cast(map_.required_span_size())); } constexpr mdarray(const mapping_type& m, const container_type& ctr) : map_(m), ctr_(ctr) { assert(ctr.size() >= static_cast(map_.required_span_size())); } MDSPAN_FUNCTION_REQUIRES( (MDSPAN_INLINE_FUNCTION constexpr), mdarray, (const extents_type& exts, container_type&& ctr), , /* requires */ (_MDSPAN_TRAIT( std::is_constructible, mapping_type, extents_type)) ) : map_(exts), ctr_(std::move(ctr)) { assert(ctr_.size() >= static_cast(map_.required_span_size())); } constexpr mdarray(const mapping_type& m, container_type&& ctr) : map_(m), ctr_(std::move(ctr)) { assert(ctr_.size() >= static_cast(map_.required_span_size())); } MDSPAN_TEMPLATE_REQUIRES( class OtherElementType, class OtherExtents, class OtherLayoutPolicy, class OtherContainer, /* requires */ ( _MDSPAN_TRAIT( std::is_constructible, mapping_type, typename OtherLayoutPolicy::template mapping) && _MDSPAN_TRAIT( std::is_constructible, container_type, OtherContainer) ) ) MDSPAN_INLINE_FUNCTION constexpr mdarray(const mdarray& other) : map_(other.mapping()), ctr_(other.container()) { static_assert( std::is_constructible::value, ""); } // Constructors for container types constructible from a size and allocator MDSPAN_TEMPLATE_REQUIRES( class Alloc, /* requires */ (_MDSPAN_TRAIT( std::is_constructible, container_type, size_t, Alloc) && _MDSPAN_TRAIT( std::is_constructible, mapping_type, extents_type)) ) MDSPAN_INLINE_FUNCTION constexpr mdarray(const extents_type& exts, const Alloc& a) : map_(exts), ctr_(map_.required_span_size(), a) { } MDSPAN_TEMPLATE_REQUIRES( class Alloc, /* requires */ (_MDSPAN_TRAIT( std::is_constructible, container_type, size_t, Alloc)) ) MDSPAN_INLINE_FUNCTION constexpr mdarray(const mapping_type& map, const Alloc& a) : map_(map), ctr_(map_.required_span_size(), a) { } // Constructors for container types constructible from a container and allocator MDSPAN_TEMPLATE_REQUIRES( class Alloc, /* requires */ (_MDSPAN_TRAIT( std::is_constructible, container_type, container_type, Alloc) && _MDSPAN_TRAIT( std::is_constructible, mapping_type, extents_type)) ) MDSPAN_INLINE_FUNCTION constexpr mdarray(const extents_type& exts, const container_type& ctr, const Alloc& a) : map_(exts), ctr_(ctr, a) { assert(ctr_.size() >= static_cast(map_.required_span_size())); } MDSPAN_TEMPLATE_REQUIRES( class Alloc, /* requires */ (_MDSPAN_TRAIT( std::is_constructible, container_type, size_t, Alloc)) ) MDSPAN_INLINE_FUNCTION constexpr mdarray(const mapping_type& map, const container_type& ctr, const Alloc& a) : map_(map), ctr_(ctr, a) { assert(ctr_.size() >= static_cast(map_.required_span_size())); } MDSPAN_TEMPLATE_REQUIRES( class Alloc, /* requires */ (_MDSPAN_TRAIT( std::is_constructible, container_type, container_type, Alloc) && _MDSPAN_TRAIT( std::is_constructible, mapping_type, extents_type)) ) MDSPAN_INLINE_FUNCTION constexpr mdarray(const extents_type& exts, container_type&& ctr, const Alloc& a) : map_(exts), ctr_(std::move(ctr), a) { assert(ctr_.size() >= static_cast(map_.required_span_size())); } MDSPAN_TEMPLATE_REQUIRES( class Alloc, /* requires */ (_MDSPAN_TRAIT( std::is_constructible, container_type, size_t, Alloc)) ) MDSPAN_INLINE_FUNCTION constexpr mdarray(const mapping_type& map, container_type&& ctr, const Alloc& a) : map_(map), ctr_(std::move(ctr), a) { assert(ctr_.size() >= map_.required_span_size()); } MDSPAN_TEMPLATE_REQUIRES( class OtherElementType, class OtherExtents, class OtherLayoutPolicy, class OtherContainer, class Alloc, /* requires */ ( _MDSPAN_TRAIT( std::is_constructible, mapping_type, typename OtherLayoutPolicy::template mapping) && _MDSPAN_TRAIT( std::is_constructible, container_type, OtherContainer, Alloc) ) ) MDSPAN_INLINE_FUNCTION constexpr mdarray(const mdarray& other, const Alloc& a) : map_(other.mapping()), ctr_(other.container(), a) { static_assert( std::is_constructible::value, ""); } MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mdarray& operator= (const mdarray&) = default; MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mdarray& operator= (mdarray&&) = default; MDSPAN_INLINE_FUNCTION_DEFAULTED ~mdarray() = default; //-------------------------------------------------------------------------------- // [mdspan.basic.mapping], mdspan mapping domain multidimensional index to access codomain element #if MDSPAN_USE_BRACKET_OPERATOR MDSPAN_TEMPLATE_REQUIRES( class... SizeTypes, /* requires */ ( _MDSPAN_FOLD_AND(_MDSPAN_TRAIT( std::is_convertible, SizeTypes, index_type) /* && ... */) && extents_type::rank() == sizeof...(SizeTypes) ) ) MDSPAN_FORCE_INLINE_FUNCTION constexpr const_reference operator[](SizeTypes... indices) const noexcept { return ctr_[map_(static_cast(std::move(indices))...)]; } MDSPAN_TEMPLATE_REQUIRES( class... SizeTypes, /* requires */ ( _MDSPAN_FOLD_AND(_MDSPAN_TRAIT( std::is_convertible, SizeTypes, index_type) /* && ... */) && extents_type::rank() == sizeof...(SizeTypes) ) ) MDSPAN_FORCE_INLINE_FUNCTION constexpr reference operator[](SizeTypes... indices) noexcept { return ctr_[map_(static_cast(std::move(indices))...)]; } #endif #if 0 MDSPAN_TEMPLATE_REQUIRES( class SizeType, size_t N, /* requires */ ( _MDSPAN_TRAIT( std::is_convertible, SizeType, index_type) && N == extents_type::rank() ) ) MDSPAN_FORCE_INLINE_FUNCTION constexpr const_reference operator[](const std::array& indices) const noexcept { return __impl::template __callop(*this, indices); } MDSPAN_TEMPLATE_REQUIRES( class SizeType, size_t N, /* requires */ ( _MDSPAN_TRAIT( std::is_convertible, SizeType, index_type) && N == extents_type::rank() ) ) MDSPAN_FORCE_INLINE_FUNCTION constexpr reference operator[](const std::array& indices) noexcept { return __impl::template __callop(*this, indices); } #endif #if MDSPAN_USE_PAREN_OPERATOR MDSPAN_TEMPLATE_REQUIRES( class... SizeTypes, /* requires */ ( (::MDSPAN_IMPL_STANDARD_NAMESPACE::detail::are_valid_indices()) && extents_type::rank() == sizeof...(SizeTypes) ) ) MDSPAN_FORCE_INLINE_FUNCTION constexpr const_reference operator()(SizeTypes... indices) const noexcept { return ctr_[map_(static_cast(std::move(indices))...)]; } MDSPAN_TEMPLATE_REQUIRES( class... SizeTypes, /* requires */ ( (::MDSPAN_IMPL_STANDARD_NAMESPACE::detail::are_valid_indices()) && extents_type::rank() == sizeof...(SizeTypes) ) ) MDSPAN_FORCE_INLINE_FUNCTION constexpr reference operator()(SizeTypes... indices) noexcept { return ctr_[map_(static_cast(std::move(indices))...)]; } #if 0 MDSPAN_TEMPLATE_REQUIRES( class SizeType, size_t N, /* requires */ ( _MDSPAN_TRAIT( std::is_convertible, SizeType, index_type) && N == extents_type::rank() ) ) MDSPAN_FORCE_INLINE_FUNCTION constexpr const_reference operator()(const std::array& indices) const noexcept { return __impl::template __callop(*this, indices); } MDSPAN_TEMPLATE_REQUIRES( class SizeType, size_t N, /* requires */ ( _MDSPAN_TRAIT( std::is_convertible, SizeType, index_type) && N == extents_type::rank() ) ) MDSPAN_FORCE_INLINE_FUNCTION constexpr reference operator()(const std::array& indices) noexcept { return __impl::template __callop(*this, indices); } #endif #endif MDSPAN_INLINE_FUNCTION constexpr pointer data() noexcept { return ctr_.data(); }; MDSPAN_INLINE_FUNCTION constexpr const_pointer data() const noexcept { return ctr_.data(); }; MDSPAN_INLINE_FUNCTION constexpr container_type& container() noexcept { return ctr_; }; MDSPAN_INLINE_FUNCTION constexpr const container_type& container() const noexcept { return ctr_; }; //-------------------------------------------------------------------------------- // [mdspan.basic.domobs], mdspan observers of the domain multidimensional index space MDSPAN_INLINE_FUNCTION static constexpr rank_type rank() noexcept { return extents_type::rank(); } MDSPAN_INLINE_FUNCTION static constexpr rank_type rank_dynamic() noexcept { return extents_type::rank_dynamic(); } MDSPAN_INLINE_FUNCTION static constexpr size_t static_extent(size_t r) noexcept { return extents_type::static_extent(r); } MDSPAN_INLINE_FUNCTION constexpr const extents_type& extents() const noexcept { return map_.extents(); }; MDSPAN_INLINE_FUNCTION constexpr index_type extent(size_t r) const noexcept { return map_.extents().extent(r); }; MDSPAN_INLINE_FUNCTION constexpr index_type size() const noexcept { // return __impl::__size(*this); return ctr_.size(); }; //-------------------------------------------------------------------------------- // [mdspan.basic.obs], mdspan observers of the mapping MDSPAN_INLINE_FUNCTION static constexpr bool is_always_unique() noexcept { return mapping_type::is_always_unique(); }; MDSPAN_INLINE_FUNCTION static constexpr bool is_always_exhaustive() noexcept { return mapping_type::is_always_exhaustive(); }; MDSPAN_INLINE_FUNCTION static constexpr bool is_always_strided() noexcept { return mapping_type::is_always_strided(); }; MDSPAN_INLINE_FUNCTION constexpr const mapping_type& mapping() const noexcept { return map_; }; MDSPAN_INLINE_FUNCTION constexpr bool is_unique() const noexcept { return map_.is_unique(); }; MDSPAN_INLINE_FUNCTION constexpr bool is_exhaustive() const noexcept { return map_.is_exhaustive(); }; MDSPAN_INLINE_FUNCTION constexpr bool is_strided() const noexcept { return map_.is_strided(); }; MDSPAN_INLINE_FUNCTION constexpr index_type stride(size_t r) const { return map_.stride(r); }; // Converstion to mdspan MDSPAN_TEMPLATE_REQUIRES( class OtherElementType, class OtherExtents, class OtherLayoutType, class OtherAccessorType, /* requires */ ( _MDSPAN_TRAIT(std::is_assignable, mdspan, mdspan_type) ) ) constexpr operator mdspan () { return mdspan_type(data(), map_); } MDSPAN_TEMPLATE_REQUIRES( class OtherElementType, class OtherExtents, class OtherLayoutType, class OtherAccessorType, /* requires */ ( _MDSPAN_TRAIT(std::is_assignable, mdspan, const_mdspan_type) ) ) constexpr operator mdspan () const { return const_mdspan_type(data(), map_); } MDSPAN_TEMPLATE_REQUIRES( class OtherAccessorType = default_accessor, /* requires */ ( _MDSPAN_TRAIT(std::is_assignable, mdspan_type, mdspan) ) ) constexpr mdspan to_mdspan(const OtherAccessorType& a = default_accessor()) { return mdspan(data(), map_, a); } MDSPAN_TEMPLATE_REQUIRES( class OtherAccessorType = default_accessor, /* requires */ ( _MDSPAN_TRAIT(std::is_assignable, const_mdspan_type, mdspan) ) ) constexpr mdspan to_mdspan(const OtherAccessorType& a = default_accessor()) const { return mdspan(data(), map_, a); } private: mapping_type map_; container_type ctr_; template friend class mdarray; }; } // end namespace MDSPAN_IMPL_PROPOSED_NAMESPACE } // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/__p1684_bits/mdarray.hpp #endif // MDARRAY_HPP_ //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/mdspan/mdarray.hpp //END_FILE_INCLUDE: /home/runner/work/mdspan/mdspan/include/experimental/mdarray #endif // _MDSPAN_SINGLE_HEADER_INCLUDE_GUARD_ fenics-basix-0.9.0/cpp/basix/moments.cpp000066400000000000000000000472711470517546000201750ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson & Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #include "moments.h" #include "cell.h" #include "finite-element.h" #include "math.h" #include "quadrature.h" using namespace basix; namespace { namespace stdex = MDSPAN_IMPL_STANDARD_NAMESPACE::MDSPAN_IMPL_PROPOSED_NAMESPACE; template using mdspan_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>; template using mdarray_t = stdex::mdarray>; //---------------------------------------------------------------------------- std::vector axis_points(const cell::type celltype) { switch (celltype) { case cell::type::interval: return {1}; case cell::type::triangle: return {1, 2}; case cell::type::quadrilateral: return {1, 2}; case cell::type::tetrahedron: return {1, 2, 3}; case cell::type::hexahedron: return {1, 2, 4}; default: throw std::runtime_error( "Integrals of this entity type not yet implemented."); } } //---------------------------------------------------------------------------- /// Map points defined on a cell entity into the full cell space /// @param[in] celltype0 Parent cell type /// @param[in] celltype1 Sub-entity of `celltype0` type /// @param[in] x Coordinates defined on an entity of type `celltype1` /// @return (0) Coordinates of points in the full space of `celltype1` /// (the shape is (num_entities, num points per entity, tdim of /// celltype0) and (1) local axes on each entity (num_entities, /// entity_dim, tdim). template std::pair>, mdarray_t> map_points(const cell::type celltype0, const cell::type celltype1, mdspan_t x) { const std::size_t tdim = cell::topological_dimension(celltype0); std::size_t entity_dim = cell::topological_dimension(celltype1); std::size_t num_entities = cell::num_sub_entities(celltype0, entity_dim); std::vector> p(num_entities, mdarray_t(x.extent(0), tdim)); mdarray_t axes(num_entities, entity_dim, tdim); const std::vector axis_pts = axis_points(celltype0); for (std::size_t e = 0; e < num_entities; ++e) { // Get entity geometry const auto [entity_buffer, eshape] = cell::sub_entity_geometry(celltype0, entity_dim, e); mdspan_t entity_x(entity_buffer.data(), eshape); // Axes on the cell entity for (std::size_t i = 0; i < axes.extent(1); ++i) for (std::size_t j = 0; j < axes.extent(2); ++j) axes(e, i, j) = entity_x(axis_pts[i], j) - entity_x(0, j); // Compute x = x0 + \Delta x std::vector axes_b(axes.extent(1) * axes.extent(2)); mdspan_t axes_e(axes_b.data(), axes.extent(1), axes.extent(2)); for (std::size_t i = 0; i < axes_e.extent(0); ++i) for (std::size_t j = 0; j < axes_e.extent(1); ++j) axes_e(i, j) = axes(e, i, j); std::vector dxbuffer(x.extent(0) * axes_e.extent(1)); mdspan_t dx(dxbuffer.data(), x.extent(0), axes_e.extent(1)); math::dot(x, axes_e, dx); for (std::size_t i = 0; i < p[e].extent(0); ++i) for (std::size_t j = 0; j < p[e].extent(1); ++j) p[e](i, j) = entity_x(0, j) + dx(i, j); } return {p, axes}; } //---------------------------------------------------------------------------- } // namespace //----------------------------------------------------------------------------- template std::tuple>, std::array, std::vector>, std::array> moments::make_integral_moments(const FiniteElement& V, cell::type celltype, polyset::type ptype, std::size_t value_size, int q_deg) { const cell::type sub_celltype = V.cell_type(); const std::size_t entity_dim = cell::topological_dimension(sub_celltype); if (entity_dim == 0) throw std::runtime_error("Cannot integrate over a dimension 0 entity."); const std::size_t num_entities = cell::num_sub_entities(celltype, entity_dim); // Get the quadrature points and weights const auto [_pts, wts] = quadrature::make_quadrature( quadrature::type::Default, sub_celltype, polyset::superset(sub_celltype, V.polyset_type(), polyset::restriction(ptype, celltype, sub_celltype)), q_deg); mdspan_t pts(_pts.data(), wts.size(), _pts.size() / wts.size()); // Evaluate moment space at quadrature points assert(std::accumulate(V.value_shape().begin(), V.value_shape().end(), 1, std::multiplies{}) == 1); const auto [phib, phishape] = V.tabulate(0, pts); mdspan_t phi(phib.data(), phishape); // Pad out \phi moment is against a vector-valued function const std::size_t vdim = value_size == 1 ? 1 : entity_dim; // Storage for the interpolation matrix const std::size_t num_dofs = vdim * phi.extent(2); const std::array Dshape = {num_dofs, value_size, pts.extent(0), 1}; const std::size_t size = std::reduce(Dshape.begin(), Dshape.end(), 1, std::multiplies{}); std::vector> Db(num_entities, std::vector(size)); std::vector> D; // Map quadrature points onto facet (cell entity e) const auto [points, axes] = map_points(celltype, sub_celltype, pts); // -- Compute entity integral moments // Iterate over cell entities if (value_size == 1) { for (std::size_t e = 0; e < num_entities; ++e) { mdspan_t& _D = D.emplace_back(Db[e].data(), Dshape); for (std::size_t i = 0; i < phi.extent(2); ++i) for (std::size_t j = 0; j < wts.size(); ++j) _D(i, 0, j, 0) = phi(0, j, i, 0) * wts[j]; } } else { for (std::size_t e = 0; e < num_entities; ++e) { mdspan_t& _D = D.emplace_back(Db[e].data(), Dshape); // Loop over each 'dof' on an entity (moment basis function index) for (std::size_t i = 0; i < phi.extent(2); ++i) { // TODO: Pad-out phi and call a updated // make_dot_integral_moments // FIXME: This assumed that the moment space has a certain // mapping type for (std::size_t d = 0; d < entity_dim; ++d) { // TODO: check that dof index is correct const std::size_t dof = i * entity_dim + d; for (std::size_t j = 0; j < value_size; ++j) for (std::size_t k = 0; k < wts.size(); ++k) _D(dof, j, k, 0) = phi(0, k, i, 0) * wts[k] * axes(e, d, j); } } } } const std::array pshape = {points.front().extent(0), points.front().extent(1)}; std::vector> pb; for (const mdarray_t& p : points) pb.emplace_back(p.data(), p.data() + p.size()); return {pb, pshape, Db, Dshape}; } //---------------------------------------------------------------------------- template std::tuple>, std::array, std::vector>, std::array> moments::make_dot_integral_moments(const FiniteElement& V, cell::type celltype, polyset::type ptype, std::size_t value_size, int q_deg) { const cell::type sub_celltype = V.cell_type(); const std::size_t entity_dim = cell::topological_dimension(sub_celltype); const std::size_t num_entities = cell::num_sub_entities(celltype, entity_dim); const auto [_pts, wts] = quadrature::make_quadrature( quadrature::type::Default, sub_celltype, polyset::superset(sub_celltype, V.polyset_type(), polyset::restriction(ptype, celltype, sub_celltype)), q_deg); mdspan_t pts(_pts.data(), wts.size(), _pts.size() / wts.size()); // If this is always true, value_size input can be removed assert(std::size_t(cell::topological_dimension(celltype)) == value_size); // Evaluate moment space at quadrature points const auto [phib, phishape] = V.tabulate(0, pts); mdspan_t phi(phib.data(), phishape); assert(phi.extent(3) == entity_dim); // Note: // Number of quadrature points per entity: phi.extent(0) // Dimension of the moment space on each entity: phi.extent(1) // Value size of the moment function: phi.extent(2) // Map quadrature points onto facet (cell entity e) const auto [points, axes] = map_points(celltype, sub_celltype, pts); // Shape (num dofs, value size, num points) const std::array Dshape = {phi.extent(2), value_size, pts.extent(0), 1}; const std::size_t size = std::reduce(Dshape.begin(), Dshape.end(), 1, std::multiplies{}); std::vector> Db(num_entities, std::vector(size)); std::vector> D; // Compute entity integral moments // Iterate over cell entities for (std::size_t e = 0; e < num_entities; ++e) { mdspan_t& _D = D.emplace_back(Db[e].data(), Dshape); // Loop over each 'dof' on an entity (moment basis function index) for (std::size_t dof = 0; dof < phi.extent(2); ++dof) { // Loop over value size of function to which moment function is // applied for (std::size_t j = 0; j < value_size; ++j) { // Loop over value topological dimension of cell entity (which // is equal to phi.extent(3)) for (std::size_t d = 0; d < phi.extent(3); ++d) { // Add quadrature point on cell entity contributions for (std::size_t k = 0; k < wts.size(); ++k) _D(dof, j, k, 0) += wts[k] * phi(0, k, dof, d) * axes(e, d, j); } } } } const std::array pshape = {points.front().extent(0), points.front().extent(1)}; std::vector> pb; for (const mdarray_t& p : points) pb.emplace_back(p.data(), p.data() + p.size()); return {pb, pshape, Db, Dshape}; } //---------------------------------------------------------------------------- template std::tuple>, std::array, std::vector>, std::array> moments::make_tangent_integral_moments(const FiniteElement& V, cell::type celltype, polyset::type ptype, std::size_t value_size, int q_deg) { const cell::type sub_celltype = V.cell_type(); const std::size_t entity_dim = cell::topological_dimension(sub_celltype); const std::size_t num_entities = cell::num_sub_entities(celltype, entity_dim); const std::size_t tdim = cell::topological_dimension(celltype); // If this is always true, value_size input can be removed assert(tdim == value_size); if (entity_dim != 1) throw std::runtime_error("Tangent is only well-defined on an edge."); const auto [_pts, wts] = quadrature::make_quadrature( quadrature::type::Default, cell::type::interval, polyset::superset(sub_celltype, V.polyset_type(), polyset::restriction(ptype, celltype, sub_celltype)), q_deg); mdspan_t pts(_pts.data(), wts.size(), _pts.size() / wts.size()); // Evaluate moment space at quadrature points assert(std::accumulate(V.value_shape().begin(), V.value_shape().end(), 1, std::multiplies{}) == 1); const auto [phib, phishape] = V.tabulate(0, pts); mdspan_t phi(phib.data(), phishape); const std::array pshape = {pts.extent(0), tdim}; std::vector> pb; const std::array Dshape = {phi.extent(2), value_size, phi.extent(1), 1}; const std::size_t size = std::reduce(Dshape.begin(), Dshape.end(), 1, std::multiplies{}); std::vector> Db(num_entities, std::vector(size)); std::vector> D; // Iterate over cell entities for (std::size_t e = 0; e < num_entities; ++e) { const auto [ebuffer, eshape] = cell::sub_entity_geometry(celltype, 1, e); mdspan_t edge_x(ebuffer.data(), eshape); std::vector tangent(edge_x.extent(1)); for (std::size_t i = 0; i < edge_x.extent(1); ++i) tangent[i] = edge_x(1, i) - edge_x(0, i); // No need to normalise the tangent, as the size of this is equal to // the integral Jacobian // Map quadrature points onto triangle edge auto& _pb = pb.emplace_back(pshape[0] * pshape[1]); mdspan_t _p(_pb.data(), pshape); for (std::size_t i = 0; i < pts.extent(0); ++i) for (std::size_t j = 0; j < _p.extent(1); ++j) _p(i, j) = edge_x(0, j) + pts(i, 0) * tangent[j]; // Compute edge tangent integral moments mdspan_t& _D = D.emplace_back(Db[e].data(), Dshape); for (std::size_t i = 0; i < phi.extent(2); ++i) { for (std::size_t j = 0; j < value_size; ++j) for (std::size_t k = 0; k < wts.size(); ++k) _D(i, j, k, 0) = phi(0, k, i, 0) * wts[k] * tangent[j]; } } return {pb, pshape, Db, Dshape}; } //---------------------------------------------------------------------------- template std::tuple>, std::array, std::vector>, std::array> moments::make_normal_integral_moments(const FiniteElement& V, cell::type celltype, polyset::type ptype, std::size_t value_size, int q_deg) { const std::size_t tdim = cell::topological_dimension(celltype); assert(tdim == value_size); const cell::type sub_celltype = V.cell_type(); const std::size_t entity_dim = cell::topological_dimension(sub_celltype); const std::size_t num_entities = cell::num_sub_entities(celltype, entity_dim); if (static_cast(entity_dim) != static_cast(tdim) - 1) throw std::runtime_error("Normal is only well-defined on a facet."); // Compute quadrature points for evaluating integral const auto [_pts, wts] = quadrature::make_quadrature( quadrature::type::Default, sub_celltype, polyset::superset(sub_celltype, V.polyset_type(), polyset::restriction(ptype, celltype, sub_celltype)), q_deg); mdspan_t pts(_pts.data(), wts.size(), _pts.size() / wts.size()); // Evaluate moment space at quadrature points assert(std::accumulate(V.value_shape().begin(), V.value_shape().end(), 1, std::multiplies{}) == 1); const auto [phib, phishape] = V.tabulate(0, pts); mdspan_t phi(phib.data(), phishape); // Storage for coordinates of evaluations points in the reference cell const std::array pshape = {pts.extent(0), tdim}; std::vector> pb; // Storage for interpolation matrix const std::array Dshape = {phi.extent(2), value_size, phi.extent(1), 1}; const std::size_t size = std::reduce(Dshape.begin(), Dshape.end(), 1, std::multiplies{}); std::vector> Db(num_entities, std::vector(size)); std::vector> D; // Evaluate moment space at quadrature points // Iterate over cell entities std::array normal; for (std::size_t e = 0; e < num_entities; ++e) { // Map quadrature points onto facet (cell entity e) const auto [ebuffer, eshape] = cell::sub_entity_geometry(celltype, tdim - 1, e); mdspan_t facet_x(ebuffer.data(), eshape); auto& _pb = pb.emplace_back(pshape[0] * pshape[1]); mdspan_t _p(_pb.data(), pshape); if (tdim == 2) { // No need to normalise the normal, as the size of this is equal // to the integral jacobian std::array tangent = {facet_x(1, 0) - facet_x(0, 0), facet_x(1, 1) - facet_x(0, 1)}; for (std::size_t p = 0; p < _p.extent(0); ++p) for (std::size_t i = 0; i < _p.extent(1); ++i) _p(p, i) = facet_x(0, i) + pts(p, 0) * tangent[i]; normal = {-tangent[1], tangent[0], 0.0}; } else if (tdim == 3) { // No need to normalise the normal, as the size of this is equal // to the integral Jacobian std::array t0 = {facet_x(1, 0) - facet_x(0, 0), facet_x(1, 1) - facet_x(0, 1), facet_x(1, 2) - facet_x(0, 2)}; std::array t1 = {facet_x(2, 0) - facet_x(0, 0), facet_x(2, 1) - facet_x(0, 1), facet_x(2, 2) - facet_x(0, 2)}; for (std::size_t p = 0; p < _p.extent(0); ++p) for (std::size_t i = 0; i < _p.extent(1); ++i) _p(p, i) = facet_x(0, i) + pts(p, 0) * t0[i] + pts(p, 1) * t1[i]; normal = math::cross(t0, t1); } else throw std::runtime_error("Normal on this cell cannot be computed."); // Compute facet normal integral moments mdspan_t& _D = D.emplace_back(Db[e].data(), Dshape); for (std::size_t i = 0; i < phi.extent(2); ++i) for (std::size_t j = 0; j < value_size; ++j) for (std::size_t k = 0; k < _D.extent(2); ++k) _D(i, j, k, 0) = phi(0, k, i, 0) * wts[k] * normal[j]; } return {pb, pshape, Db, Dshape}; } //---------------------------------------------------------------------------- /// @cond template std::tuple>, std::array, std::vector>, std::array> moments::make_integral_moments(const FiniteElement&, cell::type, polyset::type, std::size_t, int); template std::tuple< std::vector>, std::array, std::vector>, std::array> moments::make_integral_moments(const FiniteElement&, cell::type, polyset::type, std::size_t, int); template std::tuple>, std::array, std::vector>, std::array> moments::make_dot_integral_moments(const FiniteElement&, cell::type, polyset::type, std::size_t, int); template std::tuple< std::vector>, std::array, std::vector>, std::array> moments::make_dot_integral_moments(const FiniteElement&, cell::type, polyset::type, std::size_t, int); template std::tuple>, std::array, std::vector>, std::array> moments::make_tangent_integral_moments(const FiniteElement&, cell::type, polyset::type, std::size_t, int); template std::tuple< std::vector>, std::array, std::vector>, std::array> moments::make_tangent_integral_moments(const FiniteElement&, cell::type, polyset::type, std::size_t, int); template std::tuple>, std::array, std::vector>, std::array> moments::make_normal_integral_moments(const FiniteElement&, cell::type, polyset::type, std::size_t, int); template std::tuple< std::vector>, std::array, std::vector>, std::array> moments::make_normal_integral_moments(const FiniteElement&, cell::type, polyset::type, std::size_t, int); /// @endcond //---------------------------------------------------------------------------- fenics-basix-0.9.0/cpp/basix/moments.h000066400000000000000000000133271470517546000176350ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson & Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #pragma once #include "cell.h" #include "polyset.h" #include #include #include #include #include namespace basix { template class FiniteElement; /// Functions to create integral moment DOFs namespace moments { /// @brief Make interpolation points and weights for simple integral /// moments. /// /// These will represent the integral of each function in the moment /// space over each sub entity of the moment space's cell type in a cell /// with the given type. For example, if the input cell type is a /// triangle, and the moment space is a P1 space on an edge, this will /// perform two integrals for each of the 3 edges of the triangle. /// /// @param moment_space The space to compute the integral moments /// against /// @param celltype The cell type of the cell on which the space is /// being defined /// @param ptype The polyset type of the element this moment is being used to /// define /// @param value_size The value size of the space being defined /// @param q_deg The quadrature degree used for the integrals /// @return (interpolation points, interpolation matrix). The indices of /// the interpolation points are (number of entities, npoints, gdim). /// The indices on the interpolation matrix are (number of entities, /// ndofs, value_size, npoints, derivative) template std::tuple>, std::array, std::vector>, std::array> make_integral_moments(const FiniteElement& moment_space, cell::type celltype, polyset::type ptype, std::size_t value_size, int q_deg); /// @brief Make interpolation points and weights for dot product /// integral moments. /// /// These will represent the integral of each function in the moment /// space over each sub entity of the moment space's cell type in a cell /// with the given type. For example, if the input cell type is a /// triangle and the moment space is a P1 space on an edge, this will /// perform two integrals for each of the 3 edges of the triangle. /// /// @todo Clarify what happens value size of the moment space is less /// than `value_size`. /// /// @param V The space to compute the integral moments against /// @param celltype The cell type of the cell on which the space is /// being defined /// @param ptype The polyset type of the element this moment is being used to /// define /// @param value_size The value size of the space being defined /// @param q_deg The quadrature degree used for the integrals /// @return (interpolation points, interpolation shape, interpolation /// matrix, interpolation shape). The indices of the interpolation /// points are (number of entities, npoints, gdim). The indices on the /// interpolation matrix are (number of entities, ndofs, value_size, /// npoints, derivative) template std::tuple>, std::array, std::vector>, std::array> make_dot_integral_moments(const FiniteElement& V, cell::type celltype, polyset::type ptype, std::size_t value_size, int q_deg); /// @brief Make interpolation points and weights for tangent integral /// moments. /// /// These can only be used when the moment space is defined on edges of /// the cell. /// /// @param V The space to compute the integral moments against /// @param celltype The cell type of the cell on which the space is /// being defined /// @param ptype The polyset type of the element this moment is being used to /// define /// @param value_size The value size of the space being defined the /// space /// @param q_deg The quadrature degree used for the integrals /// @return (interpolation points, interpolation shape, interpolation /// matrix, interpolation shape). The indices of the interpolation /// points are (number of entities, npoints, gdim). The indices on the /// interpolation matrix are (number of entities, ndofs, value_size, /// npoints, derivative) template std::tuple>, std::array, std::vector>, std::array> make_tangent_integral_moments(const FiniteElement& V, cell::type celltype, polyset::type ptype, std::size_t value_size, int q_deg); /// @brief Compute interpolation points and weights for normal integral /// moments. /// /// These can only be used when the moment space is defined on facets of /// the cell. /// /// @param[in] V The space to compute the integral moments against /// @param[in] celltype The cell type of the cell on which the space is /// being defined /// @param ptype The polyset type of the element this moment is being used to /// define /// @param[in] value_size The value size of the space being defined /// @param[in] q_deg The quadrature degree used for the integrals /// @return (interpolation points, interpolation shape, interpolation /// matrix, interpolation shape). The indices of the interpolation /// points are (number of entities, npoints, gdim). The indices on the /// interpolation matrix are (number of entities, ndofs, value_size, /// npoints, derivative) template std::tuple>, std::array, std::vector>, std::array> make_normal_integral_moments(const FiniteElement& V, cell::type celltype, polyset::type ptype, std::size_t value_size, int q_deg); } // namespace moments } // namespace basix fenics-basix-0.9.0/cpp/basix/polynomials.cpp000066400000000000000000000114561470517546000210550ustar00rootroot00000000000000// Copyright (c) 2021 Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #include "polynomials.h" #include "mdspan.hpp" #include "polyset.h" #include #include #include #include using namespace basix; namespace stdex = MDSPAN_IMPL_STANDARD_NAMESPACE::MDSPAN_IMPL_PROPOSED_NAMESPACE; namespace { template using mdarray_t = stdex::mdarray>; template using mdspan_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>; //----------------------------------------------------------------------------- constexpr int single_choose(int n, int k) { int out = 1; for (int i = n + 1 - k; i <= n; ++i) out *= i; for (int i = 1; i <= k; ++i) out /= i; return out; } //----------------------------------------------------------------------------- int choose(int n, const std::vector& powers) { int out = 1; for (std::size_t i = 0; i < powers.size(); ++i) { out *= single_choose(n, powers[i]); n -= powers[i]; } return out; } //----------------------------------------------------------------------------- template std::pair, std::array> tabulate_bernstein(cell::type celltype, int d, mdspan_t x) { if (celltype != cell::type::interval and celltype != cell::type::triangle and celltype != cell::type::tetrahedron) { throw std::runtime_error("not implemented yet"); } // TODO: implement a better Bernstein evaluation algorithm here const std::size_t pdim = dim(polynomials::type::bernstein, celltype, d); std::array shape = {pdim, x.extent(0)}; std::vector values_b(shape[0] * shape[1]); mdspan_t values(values_b.data(), shape); mdarray_t lambdas(x.extent(1) + 1, x.extent(0)); for (std::size_t j = 0; j < lambdas.extent(1); ++j) lambdas(0, j) = 1.0; for (std::size_t i = 0; i < x.extent(1); ++i) { for (std::size_t j = 0; j < x.extent(0); ++j) { lambdas(0, j) -= x(j, i); lambdas(i + 1, j) = x(j, i); } } std::vector powers(lambdas.extent(0), 0); powers[0] = d; int n = 0; while (powers[0] >= 0) { { const int p = choose(d, powers); for (std::size_t j = 0; j < values.extent(1); ++j) values(n, j) = p; } for (std::size_t l = 0; l < lambdas.extent(0); ++l) for (int a = 0; a < powers[l]; ++a) for (std::size_t j = 0; j < values.extent(1); ++j) values(n, j) *= lambdas(l, j); powers[0] -= 1; powers[1] += 1; for (std::size_t i = 1; powers[0] < 0 and i + 1 < powers.size(); ++i) { powers[i] = 0; powers[i + 1] += 1; powers[0] = d; for (std::size_t j = 1; j < powers.size(); ++j) powers[0] -= powers[j]; } ++n; } return {std::move(values_b), std::move(shape)}; } //----------------------------------------------------------------------------- } // namespace //----------------------------------------------------------------------------- template std::pair, std::array> polynomials::tabulate( polynomials::type polytype, cell::type celltype, int d, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> x) { switch (polytype) { case polynomials::type::legendre: { auto [values, shape] = polyset::tabulate(celltype, polyset::type::standard, d, 0, x); assert(shape[0] == 1); return {std::move(values), {shape[1], shape[2]}}; } case polynomials::type::bernstein: { auto [values, shape] = tabulate_bernstein(celltype, d, x); return {std::move(values), std::move(shape)}; } default: throw std::runtime_error("not implemented yet"); } } //----------------------------------------------------------------------------- int polynomials::dim(polynomials::type, cell::type cell, int d) { return polyset::dim(cell, polyset::type::standard, d); } //----------------------------------------------------------------------------- /// @cond template std::pair, std::array> polynomials::tabulate( polynomials::type, cell::type, int, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const float, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>); template std::pair, std::array> polynomials::tabulate( polynomials::type, cell::type, int, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const double, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>); /// @endcond //----------------------------------------------------------------------------- fenics-basix-0.9.0/cpp/basix/polynomials.h000066400000000000000000000025751470517546000205240ustar00rootroot00000000000000// Copyright (c) 2021 Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #pragma once #include "cell.h" #include "mdspan.hpp" #include #include #include #include /// Polynomials namespace basix::polynomials { /// @brief Variants of a Lagrange space that can be created. enum class type { legendre = 0, bernstein = 1, }; /// @brief Tabulate a set of polynomials. /// @param[in] polytype Polynomial type /// @param[in] celltype Cell type /// @param[in] d Polynomial degree /// @param[in] x Points at which to evaluate the basis. The shape is /// (number of points, geometric dimension). /// @return Polynomial sets, for each derivative, tabulated at points. /// The shape is `(basis index, number of points)`. template std::pair, std::array> tabulate(polynomials::type polytype, cell::type celltype, int d, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> x); /// @brief Dimension of a polynomial space. /// @param[in] polytype Polynomial type /// @param[in] cell Cell type /// @param[in] d Polynomial degree /// @return The number terms in the basis spanning a space of /// polynomial degree `d`. int dim(polynomials::type polytype, cell::type cell, int d); } // namespace basix::polynomials fenics-basix-0.9.0/cpp/basix/polyset.cpp000066400000000000000000004033641470517546000202110ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson & Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #include "polyset.h" #include "cell.h" #include "indexing.h" #include "mdspan.hpp" #include #include #include #include using namespace basix; using namespace basix::indexing; namespace stdex = MDSPAN_IMPL_STANDARD_NAMESPACE::MDSPAN_IMPL_PROPOSED_NAMESPACE; namespace { //----------------------------------------------------------------------------- constexpr int single_choose(int n, int k) { int out = 1; for (int i = n + 1 - k; i <= n; ++i) out *= i; for (int i = 1; i <= k; ++i) out /= i; return out; } //----------------------------------------------------------------------------- /// Compute coefficients in the Jacobi Polynomial recurrence relation template constexpr std::array jrc(int a, int n) { T an = (a + 2 * n + 1) * (a + 2 * n + 2) / static_cast(2 * (n + 1) * (a + n + 1)); T bn = a * a * (a + 2 * n + 1) / static_cast(2 * (n + 1) * (a + n + 1) * (a + 2 * n)); T cn = n * (a + n) * (a + 2 * n + 2) / static_cast((n + 1) * (a + n + 1) * (a + 2 * n)); return {an, bn, cn}; } //----------------------------------------------------------------------------- // At a point, only the constant polynomial can be used. This has value // 1 and derivative 0. template void tabulate_polyset_point_derivs( MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> P, std::size_t, std::size_t nderiv, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> x) { assert(x.extent(0) > 0); assert(P.extent(0) == nderiv + 1); assert(P.extent(1) == 1); assert(P.extent(2) == x.extent(0)); std::fill(P.data_handle(), P.data_handle() + P.size(), 0.0); for (std::size_t i = 0; i < P.extent(2); ++i) P(0, 0, i) = 1.0; } //----------------------------------------------------------------------------- /// Compute the complete set of derivatives from 0 to nderiv, for all /// the polynomials up to order n on a line segment. The polynomials /// used are Legendre Polynomials, with the recurrence relation given by /// n P(n) = (2n - 1) x P_{n-1} - (n - 1) P_{n-2} in the interval [-1, /// 1]. The range is rescaled here to [0, 1]. template void tabulate_polyset_line_derivs( MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> P, std::size_t n, std::size_t nderiv, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> x) { assert(x.extent(0) > 0); assert(P.extent(0) == nderiv + 1); assert(P.extent(1) == n + 1); assert(P.extent(2) == x.extent(0)); std::fill(P.data_handle(), P.data_handle() + P.size(), 0.0); for (std::size_t j = 0; j < P.extent(2); ++j) P(0, 0, j) = 1.0; if (n == 0) return; auto x0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 0); for (std::size_t i = 0; i < P.extent(2); ++i) P(0, 1, i) = (x0[i] * 2.0 - 1.0) * P(0, 0, i); for (std::size_t p = 2; p <= n; ++p) { const T a = 1.0 - 1.0 / static_cast(p); for (std::size_t i = 0; i < P.extent(2); ++i) { P(0, p, i) = (x0[i] * 2.0 - 1.0) * P(0, p - 1, i) * (a + 1.0) - P(0, p - 2, i) * a; } } for (std::size_t k = 1; k <= nderiv; ++k) { for (std::size_t p = 1; p <= n; ++p) { const T a = 1.0 - 1.0 / static_cast(p); for (std::size_t i = 0; i < P.extent(2); ++i) { P(k, p, i) = (x0[i] * 2.0 - 1.0) * P(k, p - 1, i) * (a + 1.0) + 2 * k * P(k - 1, p - 1, i) * (a + 1.0) - P(k, p - 2, i) * a; } } } // Normalise for (std::size_t p = 0; p < P.extent(1); ++p) { const T norm = std::sqrt(2 * p + 1); for (std::size_t i = 0; i < P.extent(0); ++i) for (std::size_t j = 0; j < P.extent(2); ++j) P(i, p, j) *= norm; } } //----------------------------------------------------------------------------- /// Compute the complete set of derivatives from 0 to nderiv, for all /// the piecewise polynomials up to order n on a line segment split into two /// parts. template void tabulate_polyset_line_macroedge_derivs( MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> P, std::size_t n, std::size_t nderiv, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> x) { assert(x.extent(0) > 0); assert(P.extent(0) == nderiv + 1); assert(P.extent(1) == 2 * n + 1); assert(P.extent(2) == x.extent(0)); auto x0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 0); std::fill(P.data_handle(), P.data_handle() + P.size(), 0.0); std::vector factorials(n + 1, 0.0); for (std::size_t k = 0; k <= n; ++k) { factorials[k] = (k % 2 == 0 ? 1 : -1) * single_choose(2 * n + 1 - k, n - k) * single_choose(n, k) * pow(2, n - k); } for (std::size_t d = 0; d <= nderiv; ++d) { for (std::size_t p = 0; p < P.extent(2); ++p) { T value = 0.0; if (x0[p] <= 0.5) { for (std::size_t k = 0; k + d <= n; ++k) { T x_term = pow(x0[p], n - k - d); for (std::size_t i = n - k; i > n - k - d; --i) x_term *= i; value += factorials[k] * x_term; } } else { for (std::size_t k = 0; k + d <= n; ++k) { T x_term = pow(1.0 - x0[p], n - k - d); for (std::size_t i = n - k; i > n - k - d; --i) x_term *= -i; value += factorials[k] * x_term; } } P(d, 0, p) = value; } } for (std::size_t j = 0; j < n; ++j) { for (std::size_t k = 0; k <= j; ++k) { factorials[k] = (k % 2 == 0 ? 1 : -1) * single_choose(2 * n + 1 - k, j - k) * single_choose(j, k) * pow(2, j - k) * pow(2, n - j) * sqrt(4 * (n - j) + 2); } for (std::size_t d = 0; d <= nderiv; ++d) { for (std::size_t p = 0; p < P.extent(2); ++p) { if (x0[p] <= 0.5) { T value = 0.0; for (std::size_t k = 0; k + d <= j; ++k) { T x_term = pow(x0[p], j - k - d); for (std::size_t i = j - k; i > j - k - d; --i) x_term *= i; value += factorials[k] * x_term; } value *= pow(0.5 - x0[p], n - j - d); for (std::size_t i = n - j; i > n - j - d; --i) value *= -i; P(d, j + 1, p) = value; } else { T value = 0.0; for (std::size_t k = 0; k + d <= j; ++k) { T x_term = pow(1.0 - x0[p], j - k - d); for (std::size_t i = j - k; i > j - k - d; --i) x_term *= -i; value += factorials[k] * x_term; } value *= pow(x0[p] - 0.5, n - j - d); for (std::size_t i = n - j; i > n - j - d; --i) value *= i; P(d, j + n + 1, p) = value; } } } } } //----------------------------------------------------------------------------- /// Compute the complete set of derivatives from 0 to nderiv, for all /// the piecewise polynomials up to order n on a quadrilateral split into 4 by /// splitting each edge into two parts template void tabulate_polyset_quadrilateral_macroedge_derivs( MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> P, std::size_t n, std::size_t nderiv, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> x) { assert(x.extent(0) > 0); assert(P.extent(0) == (nderiv + 1) * (nderiv + 2) / 2); assert(P.extent(1) == (2 * n + 1) * (2 * n + 1)); assert(P.extent(2) == x.extent(0)); // Indexing for quadrilateral basis functions auto quad_idx = [n](std::size_t px, std::size_t py) -> std::size_t { return (2 * n + 1) * px + py; }; auto x0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 0); auto x1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 1); std::fill(P.data_handle(), P.data_handle() + P.size(), 0.0); std::vector factorials(n + 1, 0.0); // Fill with values of polynomials in x for (std::size_t k = 0; k <= n; ++k) { factorials[k] = (k % 2 == 0 ? 1 : -1) * single_choose(2 * n + 1 - k, n - k) * single_choose(n, k) * pow(2, n - k); } for (std::size_t dx = 0; dx <= nderiv; ++dx) { for (std::size_t p = 0; p < P.extent(2); ++p) { T value = 0.0; if (x0[p] <= 0.5) { for (std::size_t k = 0; k + dx <= n; ++k) { T x_term = pow(x0[p], n - k - dx); for (std::size_t i = n - k; i > n - k - dx; --i) x_term *= i; value += factorials[k] * x_term; } } else { for (std::size_t k = 0; k + dx <= n; ++k) { T x_term = pow(1.0 - x0[p], n - k - dx); for (std::size_t i = n - k; i > n - k - dx; --i) x_term *= -i; value += factorials[k] * x_term; } } for (std::size_t dy = 0; dy <= nderiv - dx; ++dy) for (std::size_t jy = 0; jy < 2 * n + 1; ++jy) P(idx(dx, dy), quad_idx(0, jy), p) = value; } } for (std::size_t j = 0; j < n; ++j) { for (std::size_t k = 0; k <= j; ++k) { factorials[k] = (k % 2 == 0 ? 1 : -1) * single_choose(2 * n + 1 - k, j - k) * single_choose(j, k) * pow(2, j - k) * pow(2, n - j) * sqrt(4 * (n - j) + 2); } for (std::size_t dx = 0; dx <= nderiv; ++dx) { for (std::size_t p = 0; p < P.extent(2); ++p) { if (x0[p] <= 0.5) { T value = 0.0; for (std::size_t k = 0; k + dx <= j; ++k) { T x_term = pow(x0[p], j - k - dx); for (std::size_t i = j - k; i > j - k - dx; --i) x_term *= i; value += factorials[k] * x_term; } value *= pow(0.5 - x0[p], n - j - dx); for (std::size_t i = n - j; i > n - j - dx; --i) value *= -i; for (std::size_t dy = 0; dy <= nderiv - dx; ++dy) for (std::size_t jy = 0; jy < 2 * n + 1; ++jy) P(idx(dx, dy), quad_idx(j + 1, jy), p) = value; } else { T value = 0.0; for (std::size_t k = 0; k + dx <= j; ++k) { T x_term = pow(1.0 - x0[p], j - k - dx); for (std::size_t i = j - k; i > j - k - dx; --i) x_term *= -i; value += factorials[k] * x_term; } value *= pow(x0[p] - 0.5, n - j - dx); for (std::size_t i = n - j; i > n - j - dx; --i) value *= i; for (std::size_t dy = 0; dy <= nderiv - dx; ++dy) for (std::size_t jy = 0; jy < 2 * n + 1; ++jy) P(idx(dx, dy), quad_idx(j + n + 1, jy), p) = value; } } } } // Multiply by values of polynomials in y for (std::size_t k = 0; k <= n; ++k) { factorials[k] = (k % 2 == 0 ? 1 : -1) * single_choose(2 * n + 1 - k, n - k) * single_choose(n, k) * pow(2, n - k); } for (std::size_t dy = 0; dy <= nderiv; ++dy) { for (std::size_t p = 0; p < P.extent(2); ++p) { T value = 0.0; if (x1[p] <= 0.5) { for (std::size_t k = 0; k + dy <= n; ++k) { T y_term = pow(x1[p], n - k - dy); for (std::size_t i = n - k; i > n - k - dy; --i) y_term *= i; value += factorials[k] * y_term; } } else { for (std::size_t k = 0; k + dy <= n; ++k) { T y_term = pow(1.0 - x1[p], n - k - dy); for (std::size_t i = n - k; i > n - k - dy; --i) y_term *= -i; value += factorials[k] * y_term; } } for (std::size_t dx = 0; dx <= nderiv - dy; ++dx) for (std::size_t jx = 0; jx < 2 * n + 1; ++jx) P(idx(dx, dy), quad_idx(jx, 0), p) *= value; } } for (std::size_t j = 0; j < n; ++j) { for (std::size_t k = 0; k <= j; ++k) { factorials[k] = (k % 2 == 0 ? 1 : -1) * single_choose(2 * n + 1 - k, j - k) * single_choose(j, k) * pow(2, j - k) * pow(2, n - j) * sqrt(4 * (n - j) + 2); } for (std::size_t dy = 0; dy <= nderiv; ++dy) { for (std::size_t p = 0; p < P.extent(2); ++p) { if (x1[p] <= 0.5) { T value = 0.0; for (std::size_t k = 0; k + dy <= j; ++k) { T y_term = pow(x1[p], j - k - dy); for (std::size_t i = j - k; i > j - k - dy; --i) y_term *= i; value += factorials[k] * y_term; } value *= pow(0.5 - x1[p], n - j - dy); for (std::size_t i = n - j; i > n - j - dy; --i) value *= -i; for (std::size_t dx = 0; dx <= nderiv - dy; ++dx) { for (std::size_t jx = 0; jx < 2 * n + 1; ++jx) { P(idx(dx, dy), quad_idx(jx, j + 1), p) *= value; P(idx(dx, dy), quad_idx(jx, j + n + 1), p) *= 0.0; } } } else { T value = 0.0; for (std::size_t k = 0; k + dy <= j; ++k) { T y_term = pow(1.0 - x1[p], j - k - dy); for (std::size_t i = j - k; i > j - k - dy; --i) y_term *= -i; value += factorials[k] * y_term; } value *= pow(x1[p] - 0.5, n - j - dy); for (std::size_t i = n - j; i > n - j - dy; --i) value *= i; for (std::size_t dx = 0; dx <= nderiv - dy; ++dx) { for (std::size_t jx = 0; jx < 2 * n + 1; ++jx) { P(idx(dx, dy), quad_idx(jx, j + 1), p) *= 0.0; P(idx(dx, dy), quad_idx(jx, j + n + 1), p) *= value; } } } } } } } //----------------------------------------------------------------------------- /// Compute the complete set of derivatives from 0 to nderiv, for all /// the piecewise polynomials up to order n on a triangle split into 4 by /// splitting each edge into two parts template void tabulate_polyset_triangle_macroedge_derivs( MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> P, std::size_t n, std::size_t nderiv, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> x) { assert(x.extent(0) > 0); assert(P.extent(0) == (nderiv + 1) * (nderiv + 2) / 2); assert(P.extent(1) == (n + 1) * (2 * n + 1)); assert(P.extent(2) == x.extent(0)); auto x0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 0); auto x1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 1); std::fill(P.data_handle(), P.data_handle() + P.size(), 0.0); if (n == 0) { for (std::size_t p = 0; p < P.extent(2); ++p) P(idx(0, 0), 0, p) = std::sqrt(2); } else if (n == 1) { for (std::size_t p = 0; p < P.extent(2); ++p) { if (x0[p] + x1[p] < 0.5) { P(idx(0, 0), 0, p) = 6.928203230275509 - 13.856406460551018 * x1[p] - 13.856406460551018 * x0[p]; P(idx(0, 0), 3, p) = -2.1908902300206643 + 4.381780460041329 * x1[p] + 13.145341380123988 * x0[p]; P(idx(0, 0), 4, p) = -1.6076739049370008 + 12.402055838085435 * x1[p] + 0.45933540141057166 * x0[p]; P(idx(0, 0), 5, p) = 1.0894095588038444 - 4.3576382352153775 * x1[p] - 4.3576382352153775 * x0[p]; if (nderiv >= 1) { P(idx(0, 1), 0, p) = -13.856406460551018; P(idx(0, 1), 3, p) = 4.381780460041329; P(idx(0, 1), 4, p) = 12.402055838085435; P(idx(0, 1), 5, p) = -4.3576382352153775; P(idx(1, 0), 0, p) = -13.856406460551018; P(idx(1, 0), 3, p) = 13.145341380123988; P(idx(1, 0), 4, p) = 0.45933540141057166; P(idx(1, 0), 5, p) = -4.3576382352153775; } } else if (x0[p] > 0.5) { P(idx(0, 0), 1, p) = -6.928203230275509 + 13.856406460551018 * x0[p]; P(idx(0, 0), 3, p) = 10.954451150103322 - 8.763560920082657 * x1[p] - 13.145341380123988 * x0[p]; P(idx(0, 0), 4, p) = -3.4450155105792875 + 2.75601240846343 * x1[p] + 4.134018612695145 * x0[p]; P(idx(0, 0), 5, p) = -0.36313651960128146 + 11.620368627241007 * x1[p] - 1.4525460784051258 * x0[p]; if (nderiv >= 1) { P(idx(0, 1), 3, p) = -8.763560920082657; P(idx(0, 1), 4, p) = 2.75601240846343; P(idx(0, 1), 5, p) = 11.620368627241007; P(idx(1, 0), 1, p) = 13.856406460551018; P(idx(1, 0), 3, p) = -13.145341380123988; P(idx(1, 0), 4, p) = 4.134018612695145; P(idx(1, 0), 5, p) = -1.4525460784051258; } } else if (x1[p] > 0.5) { P(idx(0, 0), 2, p) = -6.928203230275509 + 13.856406460551018 * x1[p]; P(idx(0, 0), 4, p) = 11.483385035264291 - 13.78006204231715 * x1[p] - 9.186708028211433 * x0[p]; P(idx(0, 0), 5, p) = -0.36313651960128146 - 1.4525460784051258 * x1[p] + 11.620368627241007 * x0[p]; if (nderiv >= 1) { P(idx(0, 1), 2, p) = 13.856406460551018; P(idx(0, 1), 4, p) = -13.78006204231715; P(idx(0, 1), 5, p) = -1.4525460784051258; P(idx(1, 0), 4, p) = -9.186708028211433; P(idx(1, 0), 5, p) = 11.620368627241007; } } else { P(idx(0, 0), 3, p) = 4.381780460041329 - 8.763560920082657 * x1[p]; P(idx(0, 0), 4, p) = 3.2153478098740016 + 2.75601240846343 * x1[p] - 9.186708028211433 * x0[p]; P(idx(0, 0), 5, p) = -6.899593872424347 + 11.620368627241007 * x1[p] + 11.620368627241007 * x0[p]; if (nderiv >= 1) { P(idx(0, 1), 3, p) = -8.763560920082657; P(idx(0, 1), 4, p) = 2.75601240846343; P(idx(0, 1), 5, p) = 11.620368627241007; P(idx(1, 0), 4, p) = -9.186708028211433; P(idx(1, 0), 5, p) = 11.620368627241007; } } } } else if (n == 2) { for (std::size_t p = 0; p < P.extent(2); ++p) { if (x0[p] + x1[p] < 0.5) { P(idx(0, 0), 0, p) = 6.928203230275509 - 13.856406460551018 * x1[p] - 13.856406460551018 * x0[p]; P(idx(0, 0), 3, p) = -2.1908902300206643 + 4.381780460041329 * x1[p] + 13.145341380123988 * x0[p]; P(idx(0, 0), 4, p) = -1.6076739049370008 + 12.402055838085435 * x1[p] + 0.45933540141057166 * x0[p]; P(idx(0, 0), 5, p) = 1.0894095588038444 - 4.3576382352153775 * x1[p] - 4.3576382352153775 * x0[p]; P(idx(0, 0), 6, p) = -8.503281219963945 + 18.006948465806 * x1[p] + 106.04091874307977 * x0[p] - 186.73872483058074 * x0[p] * x1[p] - 186.73872483058074 * x0[p] * x0[p]; P(idx(0, 0), 7, p) = -11.025314622788574 + 114.36807095525512 * x1[p] + 46.47181295366057 * x0[p] - 193.07346093997572 * x1[p] * x1[p] - 242.1245564220236 * x0[p] * x1[p] - 49.051095482047884 * x0[p] * x0[p]; P(idx(0, 0), 8, p) = 0.6108718451294817 - 6.108718451294817 * x1[p] - 6.108718451294817 * x0[p] + 12.217436902589634 * x1[p] * x1[p] + 24.43487380517927 * x0[p] * x1[p] + 12.217436902589634 * x0[p] * x0[p]; P(idx(0, 0), 9, p) = -0.9146023313861029 + 2.3428237060136627 * x1[p] + 15.949222921708396 * x0[p] + 2.117552195820041 * x1[p] * x1[p] - 36.58409325544412 * x0[p] * x1[p] - 38.701645451264156 * x0[p] * x0[p]; P(idx(0, 0), 10, p) = 0.7688034710261703 - 7.643595203265971 * x1[p] - 7.732474217257435 * x0[p] + 15.242750899536208 * x1[p] * x1[p] + 30.75213884104681 * x0[p] * x1[p] + 15.509387941510603 * x0[p] * x0[p]; P(idx(0, 0), 11, p) = -0.8466863237791316 + 15.856125699863737 * x1[p] + 1.0776007757188948 * x0[p] - 39.101513861799894 * x1[p] * x1[p] - 33.86745295116526 * x0[p] * x1[p] + 5.234060910634632 * x0[p] * x0[p]; P(idx(0, 0), 12, p) = 1.2766744019646143 - 12.766744019646143 * x1[p] - 12.766744019646143 * x0[p] + 0.785645785824378 * x1[p] * x1[p] + 150.05834509245622 * x0[p] * x1[p] + 0.785645785824378 * x0[p] * x0[p]; P(idx(0, 0), 13, p) = -1.4902171140068574 + 1.7816172183668608 * x1[p] + 28.022725061770288 * x0[p] + 5.689096337971468 * x1[p] * x1[p] - 44.1357919722882 * x0[p] * x1[p] - 73.03422719223882 * x0[p] * x0[p]; P(idx(0, 0), 14, p) = -1.7925313955666775 + 28.833058192519324 * x1[p] + 7.017569718814227 * x0[p] - 73.22681445719194 * x1[p] * x1[p] - 53.08944048146415 * x0[p] * x1[p] - 7.780349036076643 * x0[p] * x0[p]; if (nderiv >= 1) { P(idx(0, 1), 0, p) = -13.856406460551018; P(idx(0, 1), 3, p) = 4.381780460041329; P(idx(0, 1), 4, p) = 12.402055838085435; P(idx(0, 1), 5, p) = -4.3576382352153775; P(idx(0, 1), 6, p) = 18.006948465806 - 186.73872483058074 * x0[p]; P(idx(0, 1), 7, p) = 114.36807095525512 - 386.14692187995144 * x1[p] - 242.1245564220236 * x0[p]; P(idx(0, 1), 8, p) = -6.108718451294817 + 24.43487380517927 * x1[p] + 24.43487380517927 * x0[p]; P(idx(0, 1), 9, p) = 2.3428237060136627 + 4.235104391640082 * x1[p] - 36.58409325544412 * x0[p]; P(idx(0, 1), 10, p) = -7.643595203265971 + 30.485501799072416 * x1[p] + 30.75213884104681 * x0[p]; P(idx(0, 1), 11, p) = 15.856125699863737 - 78.20302772359979 * x1[p] - 33.86745295116526 * x0[p]; P(idx(0, 1), 12, p) = -12.766744019646143 + 1.571291571648756 * x1[p] + 150.05834509245622 * x0[p]; P(idx(0, 1), 13, p) = 1.7816172183668608 + 11.378192675942936 * x1[p] - 44.1357919722882 * x0[p]; P(idx(0, 1), 14, p) = 28.833058192519324 - 146.45362891438387 * x1[p] - 53.08944048146415 * x0[p]; P(idx(1, 0), 0, p) = -13.856406460551018; P(idx(1, 0), 3, p) = 13.145341380123988; P(idx(1, 0), 4, p) = 0.45933540141057166; P(idx(1, 0), 5, p) = -4.3576382352153775; P(idx(1, 0), 6, p) = 106.04091874307977 - 186.73872483058074 * x1[p] - 373.4774496611615 * x0[p]; P(idx(1, 0), 7, p) = 46.47181295366057 - 242.1245564220236 * x1[p] - 98.10219096409577 * x0[p]; P(idx(1, 0), 8, p) = -6.108718451294817 + 24.43487380517927 * x1[p] + 24.43487380517927 * x0[p]; P(idx(1, 0), 9, p) = 15.949222921708396 - 36.58409325544412 * x1[p] - 77.40329090252831 * x0[p]; P(idx(1, 0), 10, p) = -7.732474217257435 + 30.75213884104681 * x1[p] + 31.018775883021206 * x0[p]; P(idx(1, 0), 11, p) = 1.0776007757188948 - 33.86745295116526 * x1[p] + 10.468121821269264 * x0[p]; P(idx(1, 0), 12, p) = -12.766744019646143 + 150.05834509245622 * x1[p] + 1.571291571648756 * x0[p]; P(idx(1, 0), 13, p) = 28.022725061770288 - 44.1357919722882 * x1[p] - 146.06845438447763 * x0[p]; P(idx(1, 0), 14, p) = 7.017569718814227 - 53.08944048146415 * x1[p] - 15.560698072153286 * x0[p]; } if (nderiv >= 2) { P(idx(0, 2), 7, p) = -386.14692187995144; P(idx(0, 2), 8, p) = 24.43487380517927; P(idx(0, 2), 9, p) = 4.235104391640082; P(idx(0, 2), 10, p) = 30.485501799072416; P(idx(0, 2), 11, p) = -78.20302772359979; P(idx(0, 2), 12, p) = 1.571291571648756; P(idx(0, 2), 13, p) = 11.378192675942936; P(idx(0, 2), 14, p) = -146.45362891438387; P(idx(1, 1), 6, p) = -186.73872483058074; P(idx(1, 1), 7, p) = -242.1245564220236; P(idx(1, 1), 8, p) = 24.43487380517927; P(idx(1, 1), 9, p) = -36.58409325544412; P(idx(1, 1), 10, p) = 30.75213884104681; P(idx(1, 1), 11, p) = -33.86745295116526; P(idx(1, 1), 12, p) = 150.05834509245622; P(idx(1, 1), 13, p) = -44.1357919722882; P(idx(1, 1), 14, p) = -53.08944048146415; P(idx(2, 0), 6, p) = -373.4774496611615; P(idx(2, 0), 7, p) = -98.10219096409577; P(idx(2, 0), 8, p) = 24.43487380517927; P(idx(2, 0), 9, p) = -77.40329090252831; P(idx(2, 0), 10, p) = 31.018775883021206; P(idx(2, 0), 11, p) = 10.468121821269264; P(idx(2, 0), 12, p) = 1.571291571648756; P(idx(2, 0), 13, p) = -146.06845438447763; P(idx(2, 0), 14, p) = -15.560698072153286; } } else if (x0[p] > 0.5) { P(idx(0, 0), 1, p) = -6.928203230275509 + 13.856406460551018 * x0[p]; P(idx(0, 0), 3, p) = 10.954451150103322 - 8.763560920082657 * x1[p] - 13.145341380123988 * x0[p]; P(idx(0, 0), 4, p) = -3.4450155105792875 + 2.75601240846343 * x1[p] + 4.134018612695145 * x0[p]; P(idx(0, 0), 5, p) = -0.36313651960128146 + 11.620368627241007 * x1[p] - 1.4525460784051258 * x0[p]; P(idx(0, 0), 6, p) = -5.168661133703574 + 5.3353921380165925 * x1[p] + 6.002316155268667 * x0[p]; P(idx(0, 0), 7, p) = 0.19381891831812234 + 1.4014598709156538 * x1[p] - 0.4920018695767721 * x0[p]; P(idx(0, 0), 8, p) = 9.773949522071707 - 99.36848680772903 * x1[p] - 18.326155353884452 * x0[p] + 187.3340325063744 * x0[p] * x1[p]; P(idx(0, 0), 9, p) = -92.87043279242236 + 79.83622321261943 * x1[p] + 279.1564554319356 * x0[p] - 148.94952254002249 * x0[p] * x1[p] - 197.29278862757363 * x0[p] * x0[p]; P(idx(0, 0), 10, p) = 3.2129763557914517 - 28.619042505251656 * x1[p] - 5.510498867470816 * x0[p] + 42.928563757877484 * x0[p] * x1[p] + 1.2887457028762392 * x0[p] * x0[p]; P(idx(0, 0), 11, p) = 9.621435497490133 - 7.389262462072421 * x1[p] - 25.400589713373947 * x0[p] + 11.083893693108632 * x0[p] * x1[p] + 16.317954603743264 * x0[p] * x0[p]; P(idx(0, 0), 12, p) = -44.29078117584932 + 51.85262186440895 * x1[p] + 114.9006961768153 * x0[p] - 77.77893279661343 * x0[p] * x1[p] - 72.27941229584279 * x0[p] * x0[p]; P(idx(0, 0), 13, p) = -17.757288224756483 + 158.3948297834158 * x1[p] + 29.290994946031443 * x0[p] - 150.2899812849469 * x1[p] * x1[p] - 162.44725403265025 * x0[p] * x1[p] - 10.502482517762614 * x0[p] * x0[p]; P(idx(0, 0), 14, p) = -2.5553107128290935 - 15.713253935605769 * x1[p] + 8.543128353339059 * x0[p] - 27.917723011804423 * x1[p] * x1[p] + 37.528742409310865 * x0[p] * x1[p] - 7.780349036076643 * x0[p] * x0[p]; if (nderiv >= 1) { P(idx(0, 1), 3, p) = -8.763560920082657; P(idx(0, 1), 4, p) = 2.75601240846343; P(idx(0, 1), 5, p) = 11.620368627241007; P(idx(0, 1), 6, p) = 5.3353921380165925; P(idx(0, 1), 7, p) = 1.4014598709156538; P(idx(0, 1), 8, p) = -99.36848680772903 + 187.3340325063744 * x0[p]; P(idx(0, 1), 9, p) = 79.83622321261943 - 148.94952254002249 * x0[p]; P(idx(0, 1), 10, p) = -28.619042505251656 + 42.928563757877484 * x0[p]; P(idx(0, 1), 11, p) = -7.389262462072421 + 11.083893693108632 * x0[p]; P(idx(0, 1), 12, p) = 51.85262186440895 - 77.77893279661343 * x0[p]; P(idx(0, 1), 13, p) = 158.3948297834158 - 300.5799625698938 * x1[p] - 162.44725403265025 * x0[p]; P(idx(0, 1), 14, p) = -15.713253935605769 - 55.835446023608846 * x1[p] + 37.528742409310865 * x0[p]; P(idx(1, 0), 1, p) = 13.856406460551018; P(idx(1, 0), 3, p) = -13.145341380123988; P(idx(1, 0), 4, p) = 4.134018612695145; P(idx(1, 0), 5, p) = -1.4525460784051258; P(idx(1, 0), 6, p) = 6.002316155268667; P(idx(1, 0), 7, p) = -0.4920018695767721; P(idx(1, 0), 8, p) = -18.326155353884452 + 187.3340325063744 * x1[p]; P(idx(1, 0), 9, p) = 279.1564554319356 - 148.94952254002249 * x1[p] - 394.58557725514726 * x0[p]; P(idx(1, 0), 10, p) = -5.510498867470816 + 42.928563757877484 * x1[p] + 2.5774914057524785 * x0[p]; P(idx(1, 0), 11, p) = -25.400589713373947 + 11.083893693108632 * x1[p] + 32.63590920748653 * x0[p]; P(idx(1, 0), 12, p) = 114.9006961768153 - 77.77893279661343 * x1[p] - 144.55882459168558 * x0[p]; P(idx(1, 0), 13, p) = 29.290994946031443 - 162.44725403265025 * x1[p] - 21.004965035525228 * x0[p]; P(idx(1, 0), 14, p) = 8.543128353339059 + 37.528742409310865 * x1[p] - 15.560698072153286 * x0[p]; } if (nderiv >= 2) { P(idx(0, 2), 13, p) = -300.5799625698938; P(idx(0, 2), 14, p) = -55.835446023608846; P(idx(1, 1), 8, p) = 187.3340325063744; P(idx(1, 1), 9, p) = -148.94952254002249; P(idx(1, 1), 10, p) = 42.928563757877484; P(idx(1, 1), 11, p) = 11.083893693108632; P(idx(1, 1), 12, p) = -77.77893279661343; P(idx(1, 1), 13, p) = -162.44725403265025; P(idx(1, 1), 14, p) = 37.528742409310865; P(idx(2, 0), 9, p) = -394.58557725514726; P(idx(2, 0), 10, p) = 2.5774914057524785; P(idx(2, 0), 11, p) = 32.63590920748653; P(idx(2, 0), 12, p) = -144.55882459168558; P(idx(2, 0), 13, p) = -21.004965035525228; P(idx(2, 0), 14, p) = -15.560698072153286; } } else if (x1[p] > 0.5) { P(idx(0, 0), 2, p) = -6.928203230275509 + 13.856406460551018 * x1[p]; P(idx(0, 0), 4, p) = 11.483385035264291 - 13.78006204231715 * x1[p] - 9.186708028211433 * x0[p]; P(idx(0, 0), 5, p) = -0.36313651960128146 - 1.4525460784051258 * x1[p] + 11.620368627241007 * x0[p]; P(idx(0, 0), 6, p) = 1.5005790388171667 - 2.000772051756222 * x1[p]; P(idx(0, 0), 7, p) = -4.949836990893586 + 5.680385221477278 * x1[p] + 5.51638459828502 * x0[p]; P(idx(0, 0), 8, p) = 0.40724789675298784 + 0.40724789675298784 * x1[p] - 5.701470554541829 * x0[p]; P(idx(0, 0), 9, p) = 1.9981582954174217 - 2.423921449683366 * x1[p] - 1.441737665239177 * x0[p]; P(idx(0, 0), 10, p) = 10.094434014080612 - 18.67348083960675 * x1[p] - 103.09965623009914 * x0[p] + 191.9786702215639 * x0[p] * x1[p]; P(idx(0, 0), 11, p) = -93.36641006764424 + 280.33014465487247 * x1[p] + 80.35822927503759 * x0[p] - 197.97065679635696 * x1[p] * x1[p] - 149.63256485696652 * x0[p] * x1[p]; P(idx(0, 0), 12, p) = -44.29078117584932 + 114.9006961768153 * x1[p] + 51.85262186440895 * x0[p] - 72.27941229584279 * x1[p] * x1[p] - 77.77893279661343 * x0[p] * x1[p]; P(idx(0, 0), 13, p) = 0.7307650285504752 + 3.0498871026280163 * x1[p] - 44.37736718833795 * x0[p] - 5.731372000780173 * x1[p] * x1[p] + 66.56605078250692 * x0[p] * x1[p]; P(idx(0, 0), 14, p) = -17.925313955666773 + 30.358616827044155 * x1[p] + 152.86097517938816 * x0[p] - 11.746801485841205 * x1[p] * x1[p] - 152.86097517938816 * x0[p] * x1[p] - 152.86097517938816 * x0[p] * x0[p]; if (nderiv >= 1) { P(idx(0, 1), 2, p) = 13.856406460551018; P(idx(0, 1), 4, p) = -13.78006204231715; P(idx(0, 1), 5, p) = -1.4525460784051258; P(idx(0, 1), 6, p) = -2.000772051756222; P(idx(0, 1), 7, p) = 5.680385221477278; P(idx(0, 1), 8, p) = 0.40724789675298784; P(idx(0, 1), 9, p) = -2.423921449683366; P(idx(0, 1), 10, p) = -18.67348083960675 + 191.9786702215639 * x0[p]; P(idx(0, 1), 11, p) = 280.33014465487247 - 395.9413135927139 * x1[p] - 149.63256485696652 * x0[p]; P(idx(0, 1), 12, p) = 114.9006961768153 - 144.55882459168558 * x1[p] - 77.77893279661343 * x0[p]; P(idx(0, 1), 13, p) = 3.0498871026280163 - 11.462744001560345 * x1[p] + 66.56605078250692 * x0[p]; P(idx(0, 1), 14, p) = 30.358616827044155 - 23.49360297168241 * x1[p] - 152.86097517938816 * x0[p]; P(idx(1, 0), 4, p) = -9.186708028211433; P(idx(1, 0), 5, p) = 11.620368627241007; P(idx(1, 0), 7, p) = 5.51638459828502; P(idx(1, 0), 8, p) = -5.701470554541829; P(idx(1, 0), 9, p) = -1.441737665239177; P(idx(1, 0), 10, p) = -103.09965623009914 + 191.9786702215639 * x1[p]; P(idx(1, 0), 11, p) = 80.35822927503759 - 149.63256485696652 * x1[p]; P(idx(1, 0), 12, p) = 51.85262186440895 - 77.77893279661343 * x1[p]; P(idx(1, 0), 13, p) = -44.37736718833795 + 66.56605078250692 * x1[p]; P(idx(1, 0), 14, p) = 152.86097517938816 - 152.86097517938816 * x1[p] - 305.7219503587763 * x0[p]; } if (nderiv >= 2) { P(idx(0, 2), 11, p) = -395.9413135927139; P(idx(0, 2), 12, p) = -144.55882459168558; P(idx(0, 2), 13, p) = -11.462744001560345; P(idx(0, 2), 14, p) = -23.49360297168241; P(idx(1, 1), 10, p) = 191.9786702215639; P(idx(1, 1), 11, p) = -149.63256485696652; P(idx(1, 1), 12, p) = -77.77893279661343; P(idx(1, 1), 13, p) = 66.56605078250692; P(idx(1, 1), 14, p) = -152.86097517938816; P(idx(2, 0), 14, p) = -305.7219503587763; } } else { P(idx(0, 0), 3, p) = 4.381780460041329 - 8.763560920082657 * x1[p]; P(idx(0, 0), 4, p) = 3.2153478098740016 + 2.75601240846343 * x1[p] - 9.186708028211433 * x0[p]; P(idx(0, 0), 5, p) = -6.899593872424347 + 11.620368627241007 * x1[p] + 11.620368627241007 * x0[p]; P(idx(0, 0), 6, p) = -2.1675030560692408 + 5.3353921380165925 * x1[p]; P(idx(0, 0), 7, p) = -2.810374315612774 + 1.4014598709156538 * x1[p] + 5.51638459828502 * x0[p]; P(idx(0, 0), 8, p) = 3.4616071224003964 - 5.701470554541829 * x1[p] - 5.701470554541829 * x0[p]; P(idx(0, 0), 9, p) = -1.894533400728356 + 5.361461942608189 * x1[p] - 1.441737665239177 * x0[p]; P(idx(0, 0), 10, p) = 4.335073907433695 - 7.154760626312914 * x1[p] - 7.110321119317182 * x0[p]; P(idx(0, 0), 11, p) = -1.7703441315381843 - 1.8473156155181052 * x1[p] + 5.541946846554316 * x0[p]; P(idx(0, 0), 12, p) = 25.729899485748383 - 61.28037129430149 * x1[p] - 61.28037129430149 * x0[p] + 148.48705352080745 * x0[p] * x1[p]; P(idx(0, 0), 13, p) = -31.96040108338111 + 140.71152396857454 * x1[p] + 52.44597940439939 * x0[p] - 150.2899812849469 * x1[p] * x1[p] - 127.08064240296775 * x0[p] * x1[p]; P(idx(0, 0), 14, p) = -38.44407759002576 + 79.48160485874374 * x1[p] + 152.86097517938816 * x0[p] - 27.917723011804423 * x1[p] * x1[p] - 152.86097517938816 * x0[p] * x1[p] - 152.86097517938816 * x0[p] * x0[p]; if (nderiv >= 1) { P(idx(0, 1), 3, p) = -8.763560920082657; P(idx(0, 1), 4, p) = 2.75601240846343; P(idx(0, 1), 5, p) = 11.620368627241007; P(idx(0, 1), 6, p) = 5.3353921380165925; P(idx(0, 1), 7, p) = 1.4014598709156538; P(idx(0, 1), 8, p) = -5.701470554541829; P(idx(0, 1), 9, p) = 5.361461942608189; P(idx(0, 1), 10, p) = -7.154760626312914; P(idx(0, 1), 11, p) = -1.8473156155181052; P(idx(0, 1), 12, p) = -61.28037129430149 + 148.48705352080745 * x0[p]; P(idx(0, 1), 13, p) = 140.71152396857454 - 300.5799625698938 * x1[p] - 127.08064240296775 * x0[p]; P(idx(0, 1), 14, p) = 79.48160485874374 - 55.835446023608846 * x1[p] - 152.86097517938816 * x0[p]; P(idx(1, 0), 4, p) = -9.186708028211433; P(idx(1, 0), 5, p) = 11.620368627241007; P(idx(1, 0), 7, p) = 5.51638459828502; P(idx(1, 0), 8, p) = -5.701470554541829; P(idx(1, 0), 9, p) = -1.441737665239177; P(idx(1, 0), 10, p) = -7.110321119317182; P(idx(1, 0), 11, p) = 5.541946846554316; P(idx(1, 0), 12, p) = -61.28037129430149 + 148.48705352080745 * x1[p]; P(idx(1, 0), 13, p) = 52.44597940439939 - 127.08064240296775 * x1[p]; P(idx(1, 0), 14, p) = 152.86097517938816 - 152.86097517938816 * x1[p] - 305.7219503587763 * x0[p]; } if (nderiv >= 2) { P(idx(0, 2), 13, p) = -300.5799625698938; P(idx(0, 2), 14, p) = -55.835446023608846; P(idx(1, 1), 12, p) = 148.48705352080745; P(idx(1, 1), 13, p) = -127.08064240296775; P(idx(1, 1), 14, p) = -152.86097517938816; P(idx(2, 0), 14, p) = -305.7219503587763; } } } } else { throw std::runtime_error("Only degree 0 to 2 macro polysets are currently " "implemented on a triangle."); } } //----------------------------------------------------------------------------- /// Compute the complete set of derivatives from 0 to nderiv, for all /// the piecewise polynomials up to order n on a tetrahedron split into 8 by /// splitting each edge into two parts template void tabulate_polyset_tetrahedron_macroedge_derivs( MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> P, std::size_t n, std::size_t nderiv, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> x) { assert(x.extent(0) > 0); assert(P.extent(0) == (nderiv + 1) * (nderiv + 2) * (nderiv + 3) / 6); assert(P.extent(1) == (n + 1) * (2 * n + 1) * (2 * n + 3) / 3); assert(P.extent(2) == x.extent(0)); auto x0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 0); auto x1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 1); auto x2 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 2); std::fill(P.data_handle(), P.data_handle() + P.size(), 0.0); if (n == 0) { for (std::size_t p = 0; p < P.extent(2); ++p) P(idx(0, 0), 0, p) = std::sqrt(6); } else if (n == 1) { for (std::size_t p = 0; p < P.extent(2); ++p) { if (x0[p] + x1[p] + x2[p] < 0.5) { P(idx(0, 0), 0, p) = 21.908902300206645 - 43.81780460041329 * x2[p] - 43.81780460041329 * x1[p] - 43.81780460041329 * x0[p]; P(idx(0, 0), 4, p) = -5.855400437691199 + 11.710800875382398 * x2[p] + 11.710800875382398 * x1[p] + 35.1324026261472 * x0[p]; P(idx(0, 0), 5, p) = -3.1326068447244277 + 6.2652136894488555 * x2[p] + 25.75698961217863 * x1[p] - 0.6961348543832062 * x0[p]; P(idx(0, 0), 6, p) = -4.079436335011508 + 32.853642355761124 * x2[p] + 3.359535805303594 * x1[p] + 4.581185189050355 * x0[p]; P(idx(0, 0), 7, p) = 1.6490105505038288 - 7.300270809207225 * x2[p] - 8.666892660787566 * x1[p] - 0.5229420350434975 * x0[p]; P(idx(0, 0), 8, p) = 3.365373344712382 - 10.492441445989503 * x2[p] - 11.258215021433038 * x1[p] - 11.903076979701279 * x0[p]; P(idx(0, 0), 9, p) = 0.8017837257372732 + 2.6726124191242437 * x2[p] - 5.3452248382484875 * x1[p] - 5.3452248382484875 * x0[p]; if (nderiv >= 1) { P(idx(0, 0, 1), 0, p) = -43.81780460041329; P(idx(0, 0, 1), 4, p) = 11.710800875382398; P(idx(0, 0, 1), 5, p) = 6.2652136894488555; P(idx(0, 0, 1), 6, p) = 32.853642355761124; P(idx(0, 0, 1), 7, p) = -7.300270809207225; P(idx(0, 0, 1), 8, p) = -10.492441445989503; P(idx(0, 0, 1), 9, p) = 2.6726124191242437; P(idx(0, 1, 0), 0, p) = -43.81780460041329; P(idx(0, 1, 0), 4, p) = 11.710800875382398; P(idx(0, 1, 0), 5, p) = 25.75698961217863; P(idx(0, 1, 0), 6, p) = 3.359535805303594; P(idx(0, 1, 0), 7, p) = -8.666892660787566; P(idx(0, 1, 0), 8, p) = -11.258215021433038; P(idx(0, 1, 0), 9, p) = -5.3452248382484875; P(idx(1, 0, 0), 0, p) = -43.81780460041329; P(idx(1, 0, 0), 4, p) = 35.1324026261472; P(idx(1, 0, 0), 5, p) = -0.6961348543832062; P(idx(1, 0, 0), 6, p) = 4.581185189050355; P(idx(1, 0, 0), 7, p) = -0.5229420350434975; P(idx(1, 0, 0), 8, p) = -11.903076979701279; P(idx(1, 0, 0), 9, p) = -5.3452248382484875; } } else if (x0[p] > 0.5) { P(idx(0, 0), 1, p) = -21.908902300206645 + 43.81780460041329 * x0[p]; P(idx(0, 0), 4, p) = 29.277002188455995 - 23.421601750764797 * x2[p] - 23.421601750764797 * x1[p] - 35.1324026261472 * x0[p]; P(idx(0, 0), 5, p) = -8.701685679790078 + 6.961348543832062 * x2[p] + 6.961348543832062 * x1[p] + 10.442022815748093 * x0[p]; P(idx(0, 0), 6, p) = -4.472109351215823 + 3.5776874809726587 * x2[p] + 3.5776874809726587 * x1[p] + 5.366531221458988 * x0[p]; P(idx(0, 0), 7, p) = 3.4688488324552 - 2.77507906596416 * x2[p] - 2.77507906596416 * x1[p] - 4.16261859894624 * x0[p]; P(idx(0, 0), 8, p) = -1.148660363165304 + 26.439340288997876 * x2[p] + 5.172330290276515 * x1[p] - 2.8750095639459072 * x0[p]; P(idx(0, 0), 9, p) = 0.8017837257372732 + 29.398736610366683 * x1[p] - 5.3452248382484875 * x0[p]; if (nderiv >= 1) { P(idx(0, 0, 1), 4, p) = -23.421601750764797; P(idx(0, 0, 1), 5, p) = 6.961348543832062; P(idx(0, 0, 1), 6, p) = 3.5776874809726587; P(idx(0, 0, 1), 7, p) = -2.77507906596416; P(idx(0, 0, 1), 8, p) = 26.439340288997876; P(idx(0, 1, 0), 4, p) = -23.421601750764797; P(idx(0, 1, 0), 5, p) = 6.961348543832062; P(idx(0, 1, 0), 6, p) = 3.5776874809726587; P(idx(0, 1, 0), 7, p) = -2.77507906596416; P(idx(0, 1, 0), 8, p) = 5.172330290276515; P(idx(0, 1, 0), 9, p) = 29.398736610366683; P(idx(1, 0, 0), 1, p) = 43.81780460041329; P(idx(1, 0, 0), 4, p) = -35.1324026261472; P(idx(1, 0, 0), 5, p) = 10.442022815748093; P(idx(1, 0, 0), 6, p) = 5.366531221458988; P(idx(1, 0, 0), 7, p) = -4.16261859894624; P(idx(1, 0, 0), 8, p) = -2.8750095639459072; P(idx(1, 0, 0), 9, p) = -5.3452248382484875; } } else if (x1[p] > 0.5) { P(idx(0, 0), 2, p) = -21.908902300206645 + 43.81780460041329 * x1[p]; P(idx(0, 0), 5, p) = 24.36471990341222 - 19.491775922729772 * x2[p] - 29.237663884094662 * x1[p] - 19.491775922729772 * x0[p]; P(idx(0, 0), 6, p) = -5.999171080899275 + 4.79933686471942 * x2[p] + 7.199005297079131 * x1[p] + 4.79933686471942 * x0[p]; P(idx(0, 0), 7, p) = -0.4985380734081343 + 30.219077065046907 * x2[p] - 4.371795412963639 * x1[p] + 5.368871559779907 * x0[p]; P(idx(0, 0), 8, p) = -6.952417987579472 - 0.6448619582682409 * x2[p] + 9.377367643150668 * x1[p] + 4.527468332008274 * x0[p]; P(idx(0, 0), 9, p) = 0.8017837257372732 - 5.3452248382484875 * x1[p] + 29.398736610366683 * x0[p]; if (nderiv >= 1) { P(idx(0, 0, 1), 5, p) = -19.491775922729772; P(idx(0, 0, 1), 6, p) = 4.79933686471942; P(idx(0, 0, 1), 7, p) = 30.219077065046907; P(idx(0, 0, 1), 8, p) = -0.6448619582682409; P(idx(0, 1, 0), 2, p) = 43.81780460041329; P(idx(0, 1, 0), 5, p) = -29.237663884094662; P(idx(0, 1, 0), 6, p) = 7.199005297079131; P(idx(0, 1, 0), 7, p) = -4.371795412963639; P(idx(0, 1, 0), 8, p) = 9.377367643150668; P(idx(0, 1, 0), 9, p) = -5.3452248382484875; P(idx(1, 0, 0), 5, p) = -19.491775922729772; P(idx(1, 0, 0), 6, p) = 4.79933686471942; P(idx(1, 0, 0), 7, p) = 5.368871559779907; P(idx(1, 0, 0), 8, p) = 4.527468332008274; P(idx(1, 0, 0), 9, p) = 29.398736610366683; } } else if (x2[p] > 0.5) { P(idx(0, 0), 3, p) = -21.908902300206645 + 43.81780460041329 * x2[p]; P(idx(0, 0), 6, p) = 30.868462107172636 - 37.04215452860716 * x2[p] - 24.69476968573811 * x1[p] - 24.69476968573811 * x0[p]; P(idx(0, 0), 7, p) = 1.209739241067291 - 6.421728190334149 * x2[p] + 28.85245521346657 * x1[p] + 4.002249708199567 * x0[p]; P(idx(0, 0), 8, p) = -0.6784485185947118 - 2.4047977193753147 * x2[p] - 1.4106355337117769 * x1[p] + 25.0287047552861 * x0[p]; P(idx(0, 0), 9, p) = 3.474396144861517 - 2.6726124191242437 * x2[p] - 8.017837257372731 * x1[p] - 8.017837257372731 * x0[p]; if (nderiv >= 1) { P(idx(0, 0, 1), 3, p) = 43.81780460041329; P(idx(0, 0, 1), 6, p) = -37.04215452860716; P(idx(0, 0, 1), 7, p) = -6.421728190334149; P(idx(0, 0, 1), 8, p) = -2.4047977193753147; P(idx(0, 0, 1), 9, p) = -2.6726124191242437; P(idx(0, 1, 0), 6, p) = -24.69476968573811; P(idx(0, 1, 0), 7, p) = 28.85245521346657; P(idx(0, 1, 0), 8, p) = -1.4106355337117769; P(idx(0, 1, 0), 9, p) = -8.017837257372731; P(idx(1, 0, 0), 6, p) = -24.69476968573811; P(idx(1, 0, 0), 7, p) = 4.002249708199567; P(idx(1, 0, 0), 8, p) = 25.0287047552861; P(idx(1, 0, 0), 9, p) = -8.017837257372731; } } else if (x1[p] + x2[p] < 0.5 && x0[p] + x1[p] < 0.5) { P(idx(0, 0), 4, p) = 11.710800875382398 - 23.421601750764797 * x2[p] - 23.421601750764797 * x1[p]; P(idx(0, 0), 5, p) = -3.480674271916031 + 6.961348543832062 * x2[p] + 26.453124466561835 * x1[p]; P(idx(0, 0), 6, p) = 10.558541102382724 + 3.5776874809726587 * x2[p] - 25.91641906948487 * x1[p] - 24.69476968573811 * x0[p]; P(idx(0, 0), 7, p) = -0.6135853211177037 - 2.77507906596416 * x2[p] - 4.1417009175445 * x1[p] + 4.002249708199567 * x0[p]; P(idx(0, 0), 8, p) = -15.100517522781306 + 26.439340288997876 * x2[p] + 25.67356671355434 * x1[p] + 25.0287047552861 * x0[p]; P(idx(0, 0), 9, p) = 2.138089935299395 - 8.017837257372731 * x1[p] - 8.017837257372731 * x0[p]; if (nderiv >= 1) { P(idx(0, 0, 1), 4, p) = -23.421601750764797; P(idx(0, 0, 1), 5, p) = 6.961348543832062; P(idx(0, 0, 1), 6, p) = 3.5776874809726587; P(idx(0, 0, 1), 7, p) = -2.77507906596416; P(idx(0, 0, 1), 8, p) = 26.439340288997876; P(idx(0, 1, 0), 4, p) = -23.421601750764797; P(idx(0, 1, 0), 5, p) = 26.453124466561835; P(idx(0, 1, 0), 6, p) = -25.91641906948487; P(idx(0, 1, 0), 7, p) = -4.1417009175445; P(idx(0, 1, 0), 8, p) = 25.67356671355434; P(idx(0, 1, 0), 9, p) = -8.017837257372731; P(idx(1, 0, 0), 6, p) = -24.69476968573811; P(idx(1, 0, 0), 7, p) = 4.002249708199567; P(idx(1, 0, 0), 8, p) = 25.0287047552861; P(idx(1, 0, 0), 9, p) = -8.017837257372731; } } else if (x1[p] + x2[p] < 0.5) { P(idx(0, 0), 4, p) = 11.710800875382398 - 23.421601750764797 * x2[p] - 23.421601750764797 * x1[p]; P(idx(0, 0), 5, p) = 6.2652136894488555 + 6.961348543832062 * x2[p] + 6.961348543832062 * x1[p] - 19.491775922729772 * x0[p]; P(idx(0, 0), 6, p) = -4.18851217284604 + 3.5776874809726587 * x2[p] + 3.5776874809726587 * x1[p] + 4.79933686471942 * x0[p]; P(idx(0, 0), 7, p) = -1.2968962469078738 - 2.77507906596416 * x2[p] - 2.77507906596416 * x1[p] + 5.368871559779907 * x0[p]; P(idx(0, 0), 8, p) = -4.849899311142394 + 26.439340288997876 * x2[p] + 5.172330290276515 * x1[p] + 4.527468332008274 * x0[p]; P(idx(0, 0), 9, p) = -16.57019699857031 + 29.398736610366683 * x1[p] + 29.398736610366683 * x0[p]; if (nderiv >= 1) { P(idx(0, 0, 1), 4, p) = -23.421601750764797; P(idx(0, 0, 1), 5, p) = 6.961348543832062; P(idx(0, 0, 1), 6, p) = 3.5776874809726587; P(idx(0, 0, 1), 7, p) = -2.77507906596416; P(idx(0, 0, 1), 8, p) = 26.439340288997876; P(idx(0, 1, 0), 4, p) = -23.421601750764797; P(idx(0, 1, 0), 5, p) = 6.961348543832062; P(idx(0, 1, 0), 6, p) = 3.5776874809726587; P(idx(0, 1, 0), 7, p) = -2.77507906596416; P(idx(0, 1, 0), 8, p) = 5.172330290276515; P(idx(0, 1, 0), 9, p) = 29.398736610366683; P(idx(1, 0, 0), 5, p) = -19.491775922729772; P(idx(1, 0, 0), 6, p) = 4.79933686471942; P(idx(1, 0, 0), 7, p) = 5.368871559779907; P(idx(1, 0, 0), 8, p) = 4.527468332008274; P(idx(1, 0, 0), 9, p) = 29.398736610366683; } } else if (x0[p] + x1[p] > 0.5) { P(idx(0, 0), 5, p) = 19.491775922729772 - 19.491775922729772 * x2[p] - 19.491775922729772 * x1[p] - 19.491775922729772 * x0[p]; P(idx(0, 0), 6, p) = -4.79933686471942 + 4.79933686471942 * x2[p] + 4.79933686471942 * x1[p] + 4.79933686471942 * x0[p]; P(idx(0, 0), 7, p) = -17.793974312413408 + 30.219077065046907 * x2[p] + 30.219077065046907 * x1[p] + 5.368871559779907 * x0[p]; P(idx(0, 0), 8, p) = 8.692201812490664 - 0.6448619582682409 * x2[p] - 21.9118719569896 * x1[p] + 4.527468332008274 * x0[p]; P(idx(0, 0), 9, p) = -16.57019699857031 + 29.398736610366683 * x1[p] + 29.398736610366683 * x0[p]; if (nderiv >= 1) { P(idx(0, 0, 1), 5, p) = -19.491775922729772; P(idx(0, 0, 1), 6, p) = 4.79933686471942; P(idx(0, 0, 1), 7, p) = 30.219077065046907; P(idx(0, 0, 1), 8, p) = -0.6448619582682409; P(idx(0, 1, 0), 5, p) = -19.491775922729772; P(idx(0, 1, 0), 6, p) = 4.79933686471942; P(idx(0, 1, 0), 7, p) = 30.219077065046907; P(idx(0, 1, 0), 8, p) = -21.9118719569896; P(idx(0, 1, 0), 9, p) = 29.398736610366683; P(idx(1, 0, 0), 5, p) = -19.491775922729772; P(idx(1, 0, 0), 6, p) = 4.79933686471942; P(idx(1, 0, 0), 7, p) = 5.368871559779907; P(idx(1, 0, 0), 8, p) = 4.527468332008274; P(idx(1, 0, 0), 9, p) = 29.398736610366683; } } else { P(idx(0, 0), 5, p) = 9.745887961364886 - 19.491775922729772 * x2[p]; P(idx(0, 0), 6, p) = 9.947716410509344 + 4.79933686471942 * x2[p] - 24.69476968573811 * x1[p] - 24.69476968573811 * x0[p]; P(idx(0, 0), 7, p) = -17.110663386623237 + 30.219077065046907 * x2[p] + 28.85245521346657 * x1[p] + 4.002249708199567 * x0[p]; P(idx(0, 0), 8, p) = -1.5584163991482487 - 0.6448619582682409 * x2[p] - 1.4106355337117769 * x1[p] + 25.0287047552861 * x0[p]; P(idx(0, 0), 9, p) = 2.138089935299395 - 8.017837257372731 * x1[p] - 8.017837257372731 * x0[p]; if (nderiv >= 1) { P(idx(0, 0, 1), 5, p) = -19.491775922729772; P(idx(0, 0, 1), 6, p) = 4.79933686471942; P(idx(0, 0, 1), 7, p) = 30.219077065046907; P(idx(0, 0, 1), 8, p) = -0.6448619582682409; P(idx(0, 1, 0), 6, p) = -24.69476968573811; P(idx(0, 1, 0), 7, p) = 28.85245521346657; P(idx(0, 1, 0), 8, p) = -1.4106355337117769; P(idx(0, 1, 0), 9, p) = -8.017837257372731; P(idx(1, 0, 0), 6, p) = -24.69476968573811; P(idx(1, 0, 0), 7, p) = 4.002249708199567; P(idx(1, 0, 0), 8, p) = 25.0287047552861; P(idx(1, 0, 0), 9, p) = -8.017837257372731; } } } } else { throw std::runtime_error("Only degree 0 and 1 macro polysets are currently " "implemented on a tetrahedron."); } } //----------------------------------------------------------------------------- /// Compute the complete set of derivatives from 0 to nderiv, for all /// the piecewise polynomials up to order n on a hexahedron split into 4 by /// splitting each edge into two parts template void tabulate_polyset_hexahedron_macroedge_derivs( MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> P, std::size_t n, std::size_t nderiv, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> x) { assert(x.extent(0) > 0); assert(P.extent(0) == (nderiv + 1) * (nderiv + 2) * (nderiv + 3) / 6); assert(P.extent(1) == (2 * n + 1) * (2 * n + 1) * (2 * n + 1)); assert(P.extent(2) == x.extent(0)); // Indexing for hexahedral basis functions auto hex_idx = [n](std::size_t px, std::size_t py, std::size_t pz) -> std::size_t { return (2 * n + 1) * (2 * n + 1) * px + (2 * n + 1) * py + pz; }; auto x0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 0); auto x1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 1); auto x2 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 2); std::fill(P.data_handle(), P.data_handle() + P.size(), 0.0); std::vector factorials(n + 1, 0.0); // Fill with values of polynomials in x for (std::size_t k = 0; k <= n; ++k) { factorials[k] = (k % 2 == 0 ? 1 : -1) * single_choose(2 * n + 1 - k, n - k) * single_choose(n, k) * pow(2, n - k); } for (std::size_t dx = 0; dx <= nderiv; ++dx) { for (std::size_t p = 0; p < P.extent(2); ++p) { T value = 0.0; if (x0[p] <= 0.5) { for (std::size_t k = 0; k + dx <= n; ++k) { T x_term = pow(x0[p], n - k - dx); for (std::size_t i = n - k; i > n - k - dx; --i) x_term *= i; value += factorials[k] * x_term; } } else { for (std::size_t k = 0; k + dx <= n; ++k) { T x_term = pow(1.0 - x0[p], n - k - dx); for (std::size_t i = n - k; i > n - k - dx; --i) x_term *= -i; value += factorials[k] * x_term; } } for (std::size_t dy = 0; dy <= nderiv - dx; ++dy) for (std::size_t dz = 0; dz <= nderiv - dy - dx; ++dz) for (std::size_t jy = 0; jy < 2 * n + 1; ++jy) for (std::size_t jz = 0; jz < 2 * n + 1; ++jz) P(idx(dx, dy, dz), hex_idx(0, jy, jz), p) = value; } } for (std::size_t j = 0; j < n; ++j) { for (std::size_t k = 0; k <= j; ++k) { factorials[k] = (k % 2 == 0 ? 1 : -1) * single_choose(2 * n + 1 - k, j - k) * single_choose(j, k) * pow(2, j - k) * pow(2, n - j) * sqrt(4 * (n - j) + 2); } for (std::size_t dx = 0; dx <= nderiv; ++dx) { for (std::size_t p = 0; p < P.extent(2); ++p) { if (x0[p] <= 0.5) { T value = 0.0; for (std::size_t k = 0; k + dx <= j; ++k) { T x_term = pow(x0[p], j - k - dx); for (std::size_t i = j - k; i > j - k - dx; --i) x_term *= i; value += factorials[k] * x_term; } value *= pow(0.5 - x0[p], n - j - dx); for (std::size_t i = n - j; i > n - j - dx; --i) value *= -i; for (std::size_t dy = 0; dy <= nderiv - dx; ++dy) for (std::size_t dz = 0; dz <= nderiv - dy - dx; ++dz) for (std::size_t jy = 0; jy < 2 * n + 1; ++jy) for (std::size_t jz = 0; jz < 2 * n + 1; ++jz) P(idx(dx, dy, dz), hex_idx(j + 1, jy, jz), p) = value; } else { T value = 0.0; for (std::size_t k = 0; k + dx <= j; ++k) { T x_term = pow(1.0 - x0[p], j - k - dx); for (std::size_t i = j - k; i > j - k - dx; --i) x_term *= -i; value += factorials[k] * x_term; } value *= pow(x0[p] - 0.5, n - j - dx); for (std::size_t i = n - j; i > n - j - dx; --i) value *= i; for (std::size_t dy = 0; dy <= nderiv - dx; ++dy) for (std::size_t dz = 0; dz <= nderiv - dy - dx; ++dz) for (std::size_t jy = 0; jy < 2 * n + 1; ++jy) for (std::size_t jz = 0; jz < 2 * n + 1; ++jz) P(idx(dx, dy, dz), hex_idx(j + n + 1, jy, jz), p) = value; } } } } // Multiply by values of polynomials in y for (std::size_t k = 0; k <= n; ++k) { factorials[k] = (k % 2 == 0 ? 1 : -1) * single_choose(2 * n + 1 - k, n - k) * single_choose(n, k) * pow(2, n - k); } for (std::size_t dy = 0; dy <= nderiv; ++dy) { for (std::size_t p = 0; p < P.extent(2); ++p) { T value = 0.0; if (x1[p] <= 0.5) { for (std::size_t k = 0; k + dy <= n; ++k) { T y_term = pow(x1[p], n - k - dy); for (std::size_t i = n - k; i > n - k - dy; --i) y_term *= i; value += factorials[k] * y_term; } } else { for (std::size_t k = 0; k + dy <= n; ++k) { T y_term = pow(1.0 - x1[p], n - k - dy); for (std::size_t i = n - k; i > n - k - dy; --i) y_term *= -i; value += factorials[k] * y_term; } } for (std::size_t dx = 0; dx <= nderiv - dy; ++dx) for (std::size_t dz = 0; dz <= nderiv - dx - dy; ++dz) for (std::size_t jx = 0; jx < 2 * n + 1; ++jx) for (std::size_t jz = 0; jz < 2 * n + 1; ++jz) P(idx(dx, dy, dz), hex_idx(jx, 0, jz), p) *= value; } } for (std::size_t j = 0; j < n; ++j) { for (std::size_t k = 0; k <= j; ++k) { factorials[k] = (k % 2 == 0 ? 1 : -1) * single_choose(2 * n + 1 - k, j - k) * single_choose(j, k) * pow(2, j - k) * pow(2, n - j) * sqrt(4 * (n - j) + 2); } for (std::size_t dy = 0; dy <= nderiv; ++dy) { for (std::size_t p = 0; p < P.extent(2); ++p) { if (x1[p] <= 0.5) { T value = 0.0; for (std::size_t k = 0; k + dy <= j; ++k) { T y_term = pow(x1[p], j - k - dy); for (std::size_t i = j - k; i > j - k - dy; --i) y_term *= i; value += factorials[k] * y_term; } value *= pow(0.5 - x1[p], n - j - dy); for (std::size_t i = n - j; i > n - j - dy; --i) value *= -i; for (std::size_t dx = 0; dx <= nderiv - dy; ++dx) for (std::size_t dz = 0; dz <= nderiv - dx - dy; ++dz) for (std::size_t jx = 0; jx < 2 * n + 1; ++jx) for (std::size_t jz = 0; jz < 2 * n + 1; ++jz) { P(idx(dx, dy, dz), hex_idx(jx, j + 1, jz), p) *= value; P(idx(dx, dy, dz), hex_idx(jx, j + n + 1, jz), p) *= 0.0; } } else { T value = 0.0; for (std::size_t k = 0; k + dy <= j; ++k) { T y_term = pow(1.0 - x1[p], j - k - dy); for (std::size_t i = j - k; i > j - k - dy; --i) y_term *= -i; value += factorials[k] * y_term; } value *= pow(x1[p] - 0.5, n - j - dy); for (std::size_t i = n - j; i > n - j - dy; --i) value *= i; for (std::size_t dx = 0; dx <= nderiv - dy; ++dx) { for (std::size_t dz = 0; dz <= nderiv - dx - dy; ++dz) { for (std::size_t jx = 0; jx < 2 * n + 1; ++jx) { for (std::size_t jz = 0; jz < 2 * n + 1; ++jz) { P(idx(dx, dy, dz), hex_idx(jx, j + 1, jz), p) *= 0.0; P(idx(dx, dy, dz), hex_idx(jx, j + n + 1, jz), p) *= value; } } } } } } } } // Multiply by values of polynomials in z for (std::size_t k = 0; k <= n; ++k) { factorials[k] = (k % 2 == 0 ? 1 : -1) * single_choose(2 * n + 1 - k, n - k) * single_choose(n, k) * pow(2, n - k); } for (std::size_t dz = 0; dz <= nderiv; ++dz) { for (std::size_t p = 0; p < P.extent(2); ++p) { T value = 0.0; if (x2[p] <= 0.5) { for (std::size_t k = 0; k + dz <= n; ++k) { T z_term = pow(x2[p], n - k - dz); for (std::size_t i = n - k; i > n - k - dz; --i) z_term *= i; value += factorials[k] * z_term; } } else { for (std::size_t k = 0; k + dz <= n; ++k) { T z_term = pow(1.0 - x2[p], n - k - dz); for (std::size_t i = n - k; i > n - k - dz; --i) z_term *= -i; value += factorials[k] * z_term; } } for (std::size_t dx = 0; dx <= nderiv - dz; ++dx) for (std::size_t dy = 0; dy <= nderiv - dx - dz; ++dy) for (std::size_t jx = 0; jx < 2 * n + 1; ++jx) for (std::size_t jy = 0; jy < 2 * n + 1; ++jy) P(idx(dx, dy, dz), hex_idx(jx, jy, 0), p) *= value; } } for (std::size_t j = 0; j < n; ++j) { for (std::size_t k = 0; k <= j; ++k) { factorials[k] = (k % 2 == 0 ? 1 : -1) * single_choose(2 * n + 1 - k, j - k) * single_choose(j, k) * pow(2, j - k) * pow(2, n - j) * sqrt(4 * (n - j) + 2); } for (std::size_t dz = 0; dz <= nderiv; ++dz) { for (std::size_t p = 0; p < P.extent(2); ++p) { if (x2[p] <= 0.5) { T value = 0.0; for (std::size_t k = 0; k + dz <= j; ++k) { T z_term = pow(x2[p], j - k - dz); for (std::size_t i = j - k; i > j - k - dz; --i) z_term *= i; value += factorials[k] * z_term; } value *= pow(0.5 - x2[p], n - j - dz); for (std::size_t i = n - j; i > n - j - dz; --i) value *= -i; for (std::size_t dx = 0; dx <= nderiv - dz; ++dx) for (std::size_t dy = 0; dy <= nderiv - dx - dz; ++dy) for (std::size_t jx = 0; jx < 2 * n + 1; ++jx) for (std::size_t jy = 0; jy < 2 * n + 1; ++jy) { P(idx(dx, dy, dz), hex_idx(jx, jy, j + 1), p) *= value; P(idx(dx, dy, dz), hex_idx(jx, jy, j + n + 1), p) *= 0.0; } } else { T value = 0.0; for (std::size_t k = 0; k + dz <= j; ++k) { T z_term = pow(1.0 - x2[p], j - k - dz); for (std::size_t i = j - k; i > j - k - dz; --i) z_term *= -i; value += factorials[k] * z_term; } value *= pow(x2[p] - 0.5, n - j - dz); for (std::size_t i = n - j; i > n - j - dz; --i) value *= i; for (std::size_t dx = 0; dx <= nderiv - dz; ++dx) { for (std::size_t dy = 0; dy <= nderiv - dx - dz; ++dy) { for (std::size_t jx = 0; jx < 2 * n + 1; ++jx) { for (std::size_t jy = 0; jy < 2 * n + 1; ++jy) { P(idx(dx, dy, dz), hex_idx(jx, jy, j + 1), p) *= 0.0; P(idx(dx, dy, dz), hex_idx(jx, jy, j + n + 1), p) *= value; } } } } } } } } } //----------------------------------------------------------------------------- /// Compute the complete set of derivatives from 0 to nderiv, for all /// the polynomials up to order n on a triangle in [0, 1][0, 1]. The /// polynomials P_{pq} are built up in sequence, firstly along q = 0, /// which is a line segment, as in tabulate_polyset_interval_derivs /// above, but with a change of variables. The polynomials are then /// extended in the q direction, using the relation given in Sherwin and /// Karniadakis 1995 (https://doi.org/10.1016/0045-7825(94)00745-9). template void tabulate_polyset_triangle_derivs( MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> P, std::size_t n, std::size_t nderiv, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> x) { assert(x.extent(1) == 2); auto x0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 0); auto x1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 1); assert(P.extent(0) == (nderiv + 1) * (nderiv + 2) / 2); assert(P.extent(1) == (n + 1) * (n + 2) / 2); assert(P.extent(2) == x.extent(0)); std::fill(P.data_handle(), P.data_handle() + P.size(), 0.0); if (n == 0) { for (std::size_t j = 0; j < P.extent(2); ++j) P(idx(0, 0), 0, j) = std::sqrt(2.0); return; } for (std::size_t j = 0; j < P.extent(2); ++j) P(idx(0, 0), 0, j) = 1.0; // Iterate over derivatives in increasing order, since higher // derivatives for (std::size_t kx = 0; kx <= nderiv; ++kx) { for (std::size_t ky = 0; ky <= nderiv - kx; ++ky) { for (std::size_t p = 1; p <= n; ++p) { auto p0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky), idx(0, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto p1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky), idx(0, p - 1), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); T a = static_cast(2 * p - 1) / static_cast(p); for (std::size_t i = 0; i < p0.extent(0); ++i) { p0[i] = ((x0[i] * 2.0 - 1.0) + 0.5 * (x1[i] * 2.0 - 1.0) + 0.5) * p1[i] * a; } if (kx > 0) { auto px = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx - 1, ky), idx(0, p - 1), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p0.extent(0); ++i) p0[i] += 2 * kx * a * px[i]; } if (ky > 0) { auto py = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky - 1), idx(0, p - 1), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p0.extent(0); ++i) p0[i] += ky * a * py[i]; } if (p > 1) { auto p2 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky), idx(0, p - 2), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); // y^2 terms for (std::size_t i = 0; i < p0.extent(0); ++i) { const T f3 = 1.0 - x1[i]; p0[i] -= f3 * f3 * p2[i] * (a - 1.0); } if (ky > 0) { auto p2y = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky - 1), idx(0, p - 2), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p0.extent(0); ++i) p0[i] -= ky * ((x1[i] * 2.0 - 1.0) - 1.0) * p2y[i] * (a - 1.0); } if (ky > 1) { auto p2y2 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky - 2), idx(0, p - 2), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p0.extent(0); ++i) p0[i] -= ky * (ky - 1) * p2y2[i] * (a - 1.0); } } } for (std::size_t p = 0; p < n; ++p) { auto p0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky), idx(0, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto p1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky), idx(1, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p1.extent(0); ++i) p1[i] = p0[i] * ((x1[i] * 2.0 - 1.0) * (1.5 + p) + 0.5 + p); if (ky > 0) { auto py = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky - 1), idx(0, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p1.size(); ++i) p1[i] += 2 * ky * (1.5 + p) * py[i]; } for (std::size_t q = 1; q < n - p; ++q) { const auto [a1, a2, a3] = jrc(2 * p + 1, q); auto pqp1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky), idx(q + 1, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto pqm1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky), idx(q - 1, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto pq = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky), idx(q, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < pqp1.extent(0); ++i) pqp1[i] = pq[i] * ((x1[i] * 2.0 - 1.0) * a1 + a2) - pqm1[i] * a3; if (ky > 0) { auto py = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky - 1), idx(q, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < pqp1.extent(0); ++i) pqp1[i] += 2 * ky * a1 * py[i]; } } } } } // Normalisation { for (std::size_t i = 0; i < P.extent(0); ++i) { for (std::size_t p = 0; p <= n; ++p) { for (std::size_t q = 0; q <= n - p; ++q) { const T norm = std::sqrt((p + 0.5) * (p + q + 1)) * 2; const int j = idx(q, p); for (std::size_t k = 0; k < P.extent(2); ++k) P(i, j, k) *= norm; } } } } } //----------------------------------------------------------------------------- template void tabulate_polyset_tetrahedron_derivs( MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> P, std::size_t n, std::size_t nderiv, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> x) { assert(x.extent(1) == 3); assert(P.extent(0) == (nderiv + 1) * (nderiv + 2) * (nderiv + 3) / 6); assert(P.extent(1) == (n + 1) * (n + 2) * (n + 3) / 6); assert(P.extent(2) == x.extent(0)); auto x0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 0); auto x1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 1); auto x2 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 2); // Traverse derivatives in increasing order std::fill(P.data_handle(), P.data_handle() + P.size(), 0.0); for (std::size_t i = 0; i < P.extent(2); ++i) P(idx(0, 0, 0), 0, i) = 1.0; if (n == 0) { for (std::size_t i = 0; i < P.extent(2); ++i) P(idx(0, 0, 0), 0, i) = std::sqrt(6); return; } for (std::size_t kx = 0; kx <= nderiv; ++kx) { for (std::size_t ky = 0; ky <= nderiv - kx; ++ky) { for (std::size_t kz = 0; kz <= nderiv - kx - ky; ++kz) { for (std::size_t p = 1; p <= n; ++p) { auto p00 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), idx(0, 0, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto p0m1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), idx(0, 0, p - 1), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); T a = static_cast(2 * p - 1) / static_cast(p); for (std::size_t i = 0; i < p00.size(); ++i) { p00[i] = ((x0[i] * 2.0 - 1.0) + 0.5 * ((x1[i] * 2.0 - 1.0) + (x2[i] * 2.0 - 1.0)) + 1.0) * a * p0m1[i]; } if (kx > 0) { auto p0m1x = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx - 1, ky, kz), idx(0, 0, p - 1), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p00.size(); ++i) p00[i] += 2 * kx * a * p0m1x[i]; } if (ky > 0) { auto p0m1y = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky - 1, kz), idx(0, 0, p - 1), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p00.size(); ++i) p00[i] += ky * a * p0m1y[i]; } if (kz > 0) { auto p0m1z = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz - 1), idx(0, 0, p - 1), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p00.size(); ++i) p00[i] += kz * a * p0m1z[i]; } if (p > 1) { auto p0m2 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), idx(0, 0, p - 2), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p00.size(); ++i) { T f2 = x1[i] + x2[i] - 1.0; p00[i] -= f2 * f2 * p0m2[i] * (a - 1.0); } if (ky > 0) { auto p0m2y = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky - 1, kz), idx(0, 0, p - 2), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p00.size(); ++i) { p00[i] -= ky * ((x1[i] * 2.0 - 1.0) + (x2[i] * 2.0 - 1.0)) * p0m2y[i] * (a - 1.0); } } if (ky > 1) { auto p0m2y2 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky - 2, kz), idx(0, 0, p - 2), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p00.size(); ++i) p00[i] -= ky * (ky - 1) * p0m2y2[i] * (a - 1.0); } if (kz > 0) { auto p0m2z = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz - 1), idx(0, 0, p - 2), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p00.size(); ++i) p00[i] -= kz * ((x1[i] * 2.0 - 1.0) + (x2[i] * 2.0 - 1.0)) * p0m2z[i] * (a - 1.0); } if (kz > 1) { auto p0m2z2 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz - 2), idx(0, 0, p - 2), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p00.size(); ++i) p00[i] -= kz * (kz - 1) * p0m2z2[i] * (a - 1.0); } if (ky > 0 and kz > 0) { auto p0m2yz = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky - 1, kz - 1), idx(0, 0, p - 2), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p00.size(); ++i) p00[i] -= 2.0 * ky * kz * p0m2yz[i] * (a - 1.0); } } } for (std::size_t p = 0; p < n; ++p) { auto p10 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), idx(0, 1, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto p00 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), idx(0, 0, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p10.size(); ++i) p10[i] = p00[i] * ((1.0 + (x1[i] * 2.0 - 1.0)) * p + (2.0 + (x1[i] * 2.0 - 1.0) * 3.0 + (x2[i] * 2.0 - 1.0)) * 0.5); if (ky > 0) { auto p0y = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky - 1, kz), idx(0, 0, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p10.size(); ++i) p10[i] += 2 * ky * p0y[i] * (1.5 + p); } if (kz > 0) { auto p0z = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz - 1), idx(0, 0, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p10.size(); ++i) p10[i] += kz * p0z[i]; } for (std::size_t q = 1; q < n - p; ++q) { auto [aq, bq, cq] = jrc(2 * p + 1, q); auto pq1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), idx(0, q + 1, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto pq = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), idx(0, q, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto pqm1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), idx(0, q - 1, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < pq1.size(); ++i) { T f4 = 1.0 - x2[i]; T f3 = (x1[i] * 2.0 - 1.0 + x2[i]); pq1[i] = pq[i] * (f3 * aq + f4 * bq) - pqm1[i] * f4 * f4 * cq; } if (ky > 0) { auto pqy = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky - 1, kz), idx(0, q, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < pq1.size(); ++i) pq1[i] += 2 * ky * pqy[i] * aq; } if (kz > 0) { auto pqz = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz - 1), idx(0, q, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto pq1z = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz - 1), idx(0, q - 1, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < pq1.size(); ++i) { pq1[i] += kz * pqz[i] * (aq - bq) + kz * (1.0 - (x2[i] * 2.0 - 1.0)) * pq1z[i] * cq; } } if (kz > 1) { auto pq1z2 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz - 2), idx(0, q - 1, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); // Quadratic term in z for (std::size_t i = 0; i < pq1.size(); ++i) pq1[i] -= kz * (kz - 1) * pq1z2[i] * cq; } } } for (std::size_t p = 0; p < n; ++p) { for (std::size_t q = 0; q < n - p; ++q) { auto pq = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), idx(1, q, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto pq0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), idx(0, q, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < pq.size(); ++i) { pq[i] = pq0[i] * ((1.0 + p + q) + (x2[i] * 2.0 - 1.0) * (2.0 + p + q)); } if (kz > 0) { auto pqz = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz - 1), idx(0, q, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < pq.size(); ++i) pq[i] += 2 * kz * (2.0 + p + q) * pqz[i]; } } } for (std::size_t p = 0; p + 1 < n; ++p) { for (std::size_t q = 0; q + 1 < n - p; ++q) { for (std::size_t r = 1; r < n - p - q; ++r) { auto [ar, br, cr] = jrc(2 * p + 2 * q + 2, r); auto pqr1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), idx(r + 1, q, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto pqr = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), idx(r, q, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto pqrm1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), idx(r - 1, q, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < pqr1.size(); ++i) { pqr1[i] = pqr[i] * ((x2[i] * 2.0 - 1.0) * ar + br) - pqrm1[i] * cr; } if (kz > 0) { auto pqrz = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz - 1), idx(r, q, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < pqr1.size(); ++i) pqr1[i] += 2 * kz * ar * pqrz[i]; } } } } } } } // Normalise for (std::size_t p = 0; p <= n; ++p) { for (std::size_t q = 0; q <= n - p; ++q) { for (std::size_t r = 0; r <= n - p - q; ++r) { auto pqr = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, idx(r, q, p), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < pqr.extent(0); ++i) for (std::size_t j = 0; j < pqr.extent(1); ++j) pqr(i, j) *= std::sqrt(2 * (p + 0.5) * (p + q + 1.0) * (p + q + r + 1.5)) * 2; } } } } //----------------------------------------------------------------------------- template void tabulate_polyset_pyramid_derivs( MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> P, std::size_t n, std::size_t nderiv, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> x) { assert(x.extent(1) == 3); assert(P.extent(0) == (nderiv + 1) * (nderiv + 2) * (nderiv + 3) / 6); assert(P.extent(1) == (n + 1) * (n + 2) * (2 * n + 3) / 6); assert(P.extent(2) == x.extent(0)); // Indexing for pyramidal basis functions auto pyr_idx = [n](std::size_t p, std::size_t q, std::size_t r) -> std::size_t { std::size_t rv = n - r + 1; std::size_t r0 = r * (n + 1) * (n - r + 2) + (2 * r - 1) * (r - 1) * r / 6; return r0 + p * rv + q; }; const auto x0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 0); const auto x1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 1); const auto x2 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 2); // Traverse derivatives in increasing order std::fill(P.data_handle(), P.data_handle() + P.size(), 0.0); for (std::size_t j = 0; j < P.extent(2); ++j) P(idx(0, 0, 0), pyr_idx(0, 0, 0), j) = 1.0; if (n == 0) { for (std::size_t j = 0; j < P.extent(2); ++j) P(idx(0, 0, 0), pyr_idx(0, 0, 0), j) = std::sqrt(3); return; } for (std::size_t k = 0; k <= nderiv; ++k) { for (std::size_t j = 0; j <= k; ++j) { for (std::size_t kx = 0; kx <= j; ++kx) { const std::size_t ky = j - kx; const std::size_t kz = k - j; // r = 0 for (std::size_t p = 0; p <= n; ++p) { if (p > 0) { const T a = static_cast(p - 1) / static_cast(p); auto p00 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), pyr_idx(p, 0, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto p1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), pyr_idx(p - 1, 0, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p00.size(); ++i) p00[i] = (0.5 + (x0[i] * 2.0 - 1.0) + (x2[i] * 2.0 - 1.0) * 0.5) * p1[i] * (a + 1.0); if (kx > 0) { auto p11 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx - 1, ky, kz), pyr_idx(p - 1, 0, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p00.size(); ++i) p00[i] += 2.0 * kx * p11[i] * (a + 1.0); } if (kz > 0) { auto pz = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz - 1), pyr_idx(p - 1, 0, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p00.size(); ++i) p00[i] += kz * pz[i] * (a + 1.0); } if (p > 1) { auto p2 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), pyr_idx(p - 2, 0, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p00.size(); ++i) { T f2 = 1.0 - x2[i]; p00[i] -= f2 * f2 * p2[i] * a; } if (kz > 0) { auto p2z = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz - 1), pyr_idx(p - 2, 0, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p00.size(); ++i) p00[i] += kz * (1.0 - (x2[i] * 2.0 - 1.0)) * p2z[i] * a; } if (kz > 1) { // quadratic term in z auto pz = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz - 2), pyr_idx(p - 2, 0, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p00.size(); ++i) p00[i] -= kz * (kz - 1) * pz[i] * a; } } } for (std::size_t q = 1; q < n + 1; ++q) { const T a = static_cast(q - 1) / static_cast(q); auto r_pq = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), pyr_idx(p, q, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto _p = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), pyr_idx(p, q - 1, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < r_pq.size(); ++i) { r_pq[i] = (0.5 + (x1[i] * 2.0 - 1.0) + (x2[i] * 2.0 - 1.0) * 0.5) * _p[i] * (a + 1.0); } if (ky > 0) { auto _p = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky - 1, kz), pyr_idx(p, q - 1, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < r_pq.size(); ++i) r_pq[i] += 2.0 * ky * _p[i] * (a + 1.0); } if (kz > 0) { auto _p = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz - 1), pyr_idx(p, q - 1, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < r_pq.size(); ++i) r_pq[i] += kz * _p[i] * (a + 1.0); } if (q > 1) { auto _p = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), pyr_idx(p, q - 2, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < r_pq.size(); ++i) { const T f2 = 1.0 - x2[i]; r_pq[i] -= f2 * f2 * _p[i] * a; } if (kz > 0) { auto _p = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz - 1), pyr_idx(p, q - 2, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < r_pq.size(); ++i) r_pq[i] += kz * (1.0 - (x2[i] * 2.0 - 1.0)) * _p[i] * a; } if (kz > 1) { auto _p = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz - 2), pyr_idx(p, q - 2, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < r_pq.size(); ++i) r_pq[i] -= kz * (kz - 1) * _p[i] * a; } } } } // Extend into r > 0 for (std::size_t p = 0; p < n; ++p) { for (std::size_t q = 0; q < n; ++q) { auto r_pq1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), pyr_idx(p, q, 1), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto r_pq0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), pyr_idx(p, q, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < r_pq1.size(); ++i) { r_pq1[i] = r_pq0[i] * ((1.0 + p + q) + (x2[i] * 2.0 - 1.0) * (2.0 + p + q)); } if (kz > 0) { auto r_pq = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz - 1), pyr_idx(p, q, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < r_pq1.size(); ++i) r_pq1[i] += 2 * kz * r_pq[i] * (2.0 + p + q); } } } for (std::size_t r = 1; r <= n; ++r) { for (std::size_t p = 0; p < n - r; ++p) { for (std::size_t q = 0; q < n - r; ++q) { auto [ar, br, cr] = jrc(2 * p + 2 * q + 2, r); auto r_pqr = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), pyr_idx(p, q, r + 1), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto _r0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), pyr_idx(p, q, r), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto _r1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), pyr_idx(p, q, r - 1), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < r_pqr.size(); ++i) { r_pqr[i] = _r0[i] * ((x2[i] * 2.0 - 1.0) * ar + br) - _r1[i] * cr; } if (kz > 0) { auto _r = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz - 1), pyr_idx(p, q, r), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < r_pqr.size(); ++i) r_pqr[i] += ar * 2 * kz * _r[i]; } } } } } } } for (std::size_t r = 0; r <= n; ++r) { for (std::size_t p = 0; p <= n - r; ++p) { for (std::size_t q = 0; q <= n - r; ++q) { auto pqr = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, pyr_idx(p, q, r), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < pqr.extent(0); ++i) for (std::size_t j = 0; j < pqr.extent(1); ++j) pqr(i, j) *= std::sqrt(2 * (q + 0.5) * (p + 0.5) * (p + q + r + 1.5)) * 2; } } } } //----------------------------------------------------------------------------- template void tabulate_polyset_quad_derivs( MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> P, std::size_t n, std::size_t nderiv, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> x) { assert(x.extent(1) == 2); assert(P.extent(0) == (nderiv + 1) * (nderiv + 2) / 2); assert(P.extent(1) == (n + 1) * (n + 1)); assert(P.extent(2) == x.extent(0)); // Indexing for quadrilateral basis functions auto quad_idx = [n](std::size_t px, std::size_t py) -> std::size_t { return (n + 1) * px + py; }; // Compute 1D basis auto x0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 0); auto x1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 1); assert(x0.extent(0) > 0); assert(x1.extent(0) > 0); // Compute tabulation of interval for px = 0 std::fill(P.data_handle(), P.data_handle() + P.size(), 0.0); for (std::size_t j = 0; j < P.extent(2); ++j) P(idx(0, 0), quad_idx(0, 0), j) = 1.0; if (n == 0) return; { // scope auto result = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(0, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < result.extent(1); ++i) { result(quad_idx(0, 1), i) = (x1[i] * 2.0 - 1.0) * result(quad_idx(0, 0), i); } for (std::size_t py = 2; py <= n; ++py) { const T a = 1.0 - 1.0 / static_cast(py); for (std::size_t i = 0; i < result.extent(1); ++i) { result(quad_idx(0, py), i) = (x1[i] * 2.0 - 1.0) * result(quad_idx(0, py - 1), i) * (a + 1.0) - result(quad_idx(0, py - 2), i) * a; } } } for (std::size_t ky = 1; ky <= nderiv; ++ky) { // Get reference to this derivative auto result = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(0, ky), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto result0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(0, ky - 1), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < result.extent(1); ++i) { result(quad_idx(0, 1), i) = (x1[i] * 2.0 - 1.0) * result(quad_idx(0, 0), i) + 2 * ky * result0(quad_idx(0, 0), i); } for (std::size_t py = 2; py <= n; ++py) { const T a = 1.0 - 1.0 / static_cast(py); for (std::size_t i = 0; i < result.extent(1); ++i) { result(quad_idx(0, py), i) = (x1[i] * 2.0 - 1.0) * result(quad_idx(0, py - 1), i) * (a + 1.0) + 2 * ky * result0(quad_idx(0, py - 1), i) * (a + 1.0) - result(quad_idx(0, py - 2), i) * a; } } } // Take tensor product with another interval for (std::size_t ky = 0; ky <= nderiv; ++ky) { auto result = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(0, ky), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t py = 0; py <= n; ++py) { for (std::size_t i = 0; i < result.extent(1); ++i) { result(quad_idx(1, py), i) = (x0[i] * 2.0 - 1.0) * result(quad_idx(0, py), i); } } } for (std::size_t px = 2; px <= n; ++px) { const T a = 1.0 - 1.0 / static_cast(px); for (std::size_t ky = 0; ky <= nderiv; ++ky) { auto result = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(0, ky), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t py = 0; py <= n; ++py) { for (std::size_t i = 0; i < result.extent(1); ++i) { result(quad_idx(px, py), i) = (x0[i] * 2.0 - 1.0) * result(quad_idx(px - 1, py), i) * (a + 1.0) - result(quad_idx(px - 2, py), i) * a; } } } } for (std::size_t kx = 1; kx <= nderiv; ++kx) { for (std::size_t ky = 0; ky <= nderiv - kx; ++ky) { auto result = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto result0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx - 1, ky), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t py = 0; py <= n; ++py) { for (std::size_t i = 0; i < result.extent(1); ++i) { result(quad_idx(1, py), i) = (x0[i] * 2.0 - 1.0) * result(quad_idx(0, py), i) + 2 * kx * result0(quad_idx(0, py), i); } } } for (std::size_t px = 2; px <= n; ++px) { const T a = 1.0 - 1.0 / static_cast(px); for (std::size_t ky = 0; ky <= nderiv - kx; ++ky) { auto result = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto result0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx - 1, ky), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t py = 0; py <= n; ++py) { for (std::size_t i = 0; i < result.extent(1); ++i) { result(quad_idx(px, py), i) = (x0[i] * 2.0 - 1.0) * result(quad_idx(px - 1, py), i) * (a + 1.0) + 2 * kx * result0(quad_idx(px - 1, py), i) * (a + 1.0) - result(quad_idx(px - 2, py), i) * a; } } } } } // Normalise for (std::size_t px = 0; px <= n; ++px) { for (std::size_t py = 0; py <= n; ++py) { auto pxy = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, quad_idx(px, py), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < pxy.extent(0); ++i) for (std::size_t j = 0; j < pxy.extent(1); ++j) pxy(i, j) *= std::sqrt((2 * px + 1) * (2 * py + 1)); } } } //----------------------------------------------------------------------------- template void tabulate_polyset_hex_derivs( MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> P, std::size_t n, std::size_t nderiv, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> x) { assert(x.extent(1) == 3); assert(P.extent(0) == (nderiv + 1) * (nderiv + 2) * (nderiv + 3) / 6); assert(P.extent(1) == (n + 1) * (n + 1) * (n + 1)); assert(P.extent(2) == x.extent(0)); // Indexing for hexahedral basis functions auto hex_idx = [n](std::size_t px, std::size_t py, std::size_t pz) -> std::size_t { return (n + 1) * (n + 1) * px + (n + 1) * py + pz; }; // Compute 1D basis auto x0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 0); auto x1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 1); auto x2 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 2); assert(x0.extent(0) > 0); assert(x1.extent(0) > 0); assert(x2.extent(0) > 0); std::fill(P.data_handle(), P.data_handle() + P.size(), 0.0); for (std::size_t i = 0; i < P.extent(2); ++i) P(idx(0, 0, 0), hex_idx(0, 0, 0), i) = 1.0; if (n == 0) return; // Tabulate interval for px=py=0 // For kz = 0 { // scope auto result = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(0, 0, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); // for pz = 1 for (std::size_t i = 0; i < result.extent(1); ++i) { result(hex_idx(0, 0, 1), i) = (x2[i] * 2.0 - 1.0) * result(hex_idx(0, 0, 0), i); } // for larger values of pz for (std::size_t pz = 2; pz <= n; ++pz) { const T a = 1.0 - 1.0 / static_cast(pz); for (std::size_t i = 0; i < result.extent(1); ++i) { result(hex_idx(0, 0, pz), i) = (x2[i] * 2.0 - 1.0) * result(hex_idx(0, 0, pz - 1), i) * (a + 1.0) - result(hex_idx(0, 0, pz - 2), i) * a; } } } // for larger values of kz for (std::size_t kz = 1; kz <= nderiv; ++kz) { // Get reference to this derivative auto result = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(0, 0, kz), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto result0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(0, 0, kz - 1), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); // for pz = 1 for (std::size_t i = 0; i < result.extent(1); ++i) { result(hex_idx(0, 0, 1), i) = (x2[i] * 2.0 - 1.0) * result(hex_idx(0, 0, 0), i) + 2 * kz * result0(hex_idx(0, 0, 0), i); } // for larger values of pz for (std::size_t pz = 2; pz <= n; ++pz) { const T a = 1.0 - 1.0 / static_cast(pz); for (std::size_t i = 0; i < result.extent(1); ++i) { result(hex_idx(0, 0, pz), i) = (x2[i] * 2.0 - 1.0) * result(hex_idx(0, 0, pz - 1), i) * (a + 1.0) + 2 * kz * result0(hex_idx(0, 0, pz - 1), i) * (a + 1.0) - result(hex_idx(0, 0, pz - 2), i) * a; } } } // Take tensor product with interval to get quad for px=0 // for ky = 0 // for py = 1 for (std::size_t kz = 0; kz <= nderiv; ++kz) { auto result = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(0, 0, kz), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t pz = 0; pz <= n; ++pz) { for (std::size_t i = 0; i < result.extent(1); ++i) { result(hex_idx(0, 1, pz), i) = (x1[i] * 2.0 - 1.0) * result(hex_idx(0, 0, pz), i); } } } for (std::size_t py = 2; py <= n; ++py) { const T a = 1.0 - 1.0 / static_cast(py); for (std::size_t kz = 0; kz <= nderiv; ++kz) { auto result = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(0, 0, kz), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t pz = 0; pz <= n; ++pz) { for (std::size_t i = 0; i < result.extent(1); ++i) { result(hex_idx(0, py, pz), i) = (x1[i] * 2.0 - 1.0) * result(hex_idx(0, py - 1, pz), i) * (a + 1.0) - result(hex_idx(0, py - 2, pz), i) * a; } } } } // for larger values of ky for (std::size_t ky = 1; ky <= nderiv; ++ky) { // for py = 1 for (std::size_t kz = 0; kz <= nderiv - ky; ++kz) { auto result = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(0, ky, kz), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto result0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(0, ky - 1, kz), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t pz = 0; pz <= n; ++pz) { for (std::size_t i = 0; i < result.extent(1); ++i) { result(hex_idx(0, 1, pz), i) = (x1[i] * 2.0 - 1.0) * result(hex_idx(0, 0, pz), i) + 2 * ky * result0(hex_idx(0, 0, pz), i); } } } for (std::size_t py = 2; py <= n; ++py) { const T a = 1.0 - 1.0 / static_cast(py); for (std::size_t kz = 0; kz <= nderiv - ky; ++kz) { auto result = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(0, ky, kz), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto result0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(0, ky - 1, kz), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t pz = 0; pz <= n; ++pz) { for (std::size_t i = 0; i < result.extent(1); ++i) { result(hex_idx(0, py, pz), i) = (x1[i] * 2.0 - 1.0) * result(hex_idx(0, py - 1, pz), i) * (a + 1.0) + 2 * ky * result0(hex_idx(0, py - 1, pz), i) * (a + 1.0) - result(hex_idx(0, py - 2, pz), i) * a; } } } } } // Take tensor product with interval to get hex // kx = 0 // for px = 1 for (std::size_t ky = 0; ky <= nderiv; ++ky) { for (std::size_t kz = 0; kz <= nderiv - ky; ++kz) { auto result = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(0, ky, kz), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t py = 0; py <= n; ++py) { for (std::size_t pz = 0; pz <= n; ++pz) { for (std::size_t i = 0; i < result.extent(1); ++i) { result(hex_idx(1, py, pz), i) = (x0[i] * 2.0 - 1.0) * result(hex_idx(0, py, pz), i); } } } } } // For larger values of px for (std::size_t px = 2; px <= n; ++px) { const T a = 1.0 - 1.0 / static_cast(px); for (std::size_t ky = 0; ky <= nderiv; ++ky) { for (std::size_t kz = 0; kz <= nderiv - ky; ++kz) { auto result = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(0, ky, kz), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t py = 0; py <= n; ++py) { for (std::size_t pz = 0; pz <= n; ++pz) { for (std::size_t i = 0; i < result.extent(1); ++i) { result(hex_idx(px, py, pz), i) = (x0[i] * 2.0 - 1.0) * result(hex_idx(px - 1, py, pz), i) * (a + 1.0) - result(hex_idx(px - 2, py, pz), i) * a; } } } } } } // For larger values of kx for (std::size_t kx = 1; kx <= nderiv; ++kx) { // for px = 1 { for (std::size_t ky = 0; ky <= nderiv - kx; ++ky) { for (std::size_t kz = 0; kz <= nderiv - kx - ky; ++kz) { auto result = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto result0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx - 1, ky, kz), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t py = 0; py <= n; ++py) { for (std::size_t pz = 0; pz <= n; ++pz) { for (std::size_t i = 0; i < result.extent(1); ++i) { result(hex_idx(1, py, pz), i) = (x0[i] * 2.0 - 1.0) * result(hex_idx(0, py, pz), i) + 2 * kx * result0(hex_idx(0, py, pz), i); } } } } } } // For larger values of px for (std::size_t px = 2; px <= n; ++px) { const T a = 1.0 - 1.0 / static_cast(px); for (std::size_t ky = 0; ky <= nderiv - kx; ++ky) { for (std::size_t kz = 0; kz <= nderiv - kx - ky; ++kz) { auto result = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto result0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx - 1, ky, kz), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t py = 0; py <= n; ++py) { for (std::size_t pz = 0; pz <= n; ++pz) { for (std::size_t i = 0; i < result.extent(1); ++i) { result(hex_idx(px, py, pz), i) = (x0[i] * 2.0 - 1.0) * result(hex_idx(px - 1, py, pz), i) * (a + 1.0) + 2 * kx * result0(hex_idx(px - 1, py, pz), i) * (a + 1.0) - result(hex_idx(px - 2, py, pz), i) * a; } } } } } } } // Normalise for (std::size_t px = 0; px <= n; ++px) { for (std::size_t py = 0; py <= n; ++py) { for (std::size_t pz = 0; pz <= n; ++pz) { auto pxyz = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, hex_idx(px, py, pz), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < pxyz.extent(0); ++i) for (std::size_t j = 0; j < pxyz.extent(1); ++j) pxyz(i, j) *= std::sqrt((2 * px + 1) * (2 * py + 1) * (2 * pz + 1)); } } } } //----------------------------------------------------------------------------- template void tabulate_polyset_prism_derivs( MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> P, std::size_t n, std::size_t nderiv, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> x) { assert(x.extent(1) == 3); assert(P.extent(0) == (nderiv + 1) * (nderiv + 2) * (nderiv + 3) / 6); assert(P.extent(1) == (n + 1) * (n + 1) * (n + 2) / 2); assert(P.extent(2) == x.extent(0)); auto x0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 0); auto x1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 1); auto x2 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( x, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, 2); assert(x0.extent(0) > 0); assert(x1.extent(0) > 0); assert(x2.extent(0) > 0); // Indexing for hexahedral basis functions auto prism_idx = [n](std::size_t px, std::size_t py, std::size_t pz) -> std::size_t { return (n + 1) * idx(py, px) + pz; }; // Tabulate triangle for px=0 std::fill(P.data_handle(), P.data_handle() + P.size(), 0.0); if (n == 0) { for (std::size_t i = 0; i < P.extent(2); ++i) P(idx(0, 0, 0), prism_idx(0, 0, 0), i) = std::sqrt(2); return; } for (std::size_t i = 0; i < P.extent(2); ++i) P(idx(0, 0, 0), prism_idx(0, 0, 0), i) = 1.0; for (std::size_t kx = 0; kx <= nderiv; ++kx) { for (std::size_t ky = 0; ky <= nderiv - kx; ++ky) { for (std::size_t p = 1; p <= n; ++p) { auto p0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, 0), prism_idx(p, 0, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto p1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, 0), prism_idx(p - 1, 0, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); const T a = static_cast(2 * p - 1) / static_cast(p); for (std::size_t i = 0; i < p0.size(); ++i) { p0[i] = ((x0[i] * 2.0 - 1.0) + 0.5 * (x1[i] * 2.0 - 1.0) + 0.5) * p1[i] * a; } if (kx > 0) { auto result0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx - 1, ky, 0), prism_idx(p - 1, 0, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p0.size(); ++i) p0[i] += 2 * kx * a * result0[i]; } if (ky > 0) { auto result0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky - 1, 0), prism_idx(p - 1, 0, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p0.size(); ++i) p0[i] += ky * a * result0[i]; } if (p > 1) { // y^2 terms auto p2 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, 0), prism_idx(p - 2, 0, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p0.size(); ++i) { T f2 = (1.0 - x1[i]); p0[i] -= f2 * f2 * p2[i] * (a - 1.0); } if (ky > 0) { auto result0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky - 1, 0), prism_idx(p - 2, 0, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p0.size(); ++i) { p0[i] -= ky * ((x1[i] * 2.0 - 1.0) - 1.0) * result0[i] * (a - 1.0); } } if (ky > 1) { auto result0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky - 2, 0), prism_idx(p - 2, 0, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p0.size(); ++i) p0[i] -= ky * (ky - 1) * result0[i] * (a - 1.0); } } } for (std::size_t p = 0; p < n; ++p) { auto p0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, 0), prism_idx(p, 0, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto p1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, 0), prism_idx(p, 1, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p1.size(); ++i) p1[i] = p0[i] * ((x1[i] * 2.0 - 1.0) * (1.5 + p) + 0.5 + p); if (ky > 0) { auto result0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky - 1, 0), prism_idx(p, 0, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < p1.size(); ++i) p1[i] += 2 * ky * (1.5 + p) * result0[i]; } for (std::size_t q = 1; q < n - p; ++q) { auto pqp1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, 0), prism_idx(p, q + 1, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto pq = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, 0), prism_idx(p, q, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto pqm1 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, 0), prism_idx(p, q - 1, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); const auto [a1, a2, a3] = jrc(2 * p + 1, q); for (std::size_t i = 0; i < p0.size(); ++i) pqp1[i] = pq[i] * ((x1[i] * 2.0 - 1.0) * a1 + a2) - pqm1[i] * a3; if (ky > 0) { auto result0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky - 1, 0), prism_idx(p, q, 0), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < pqp1.size(); ++i) pqp1[i] += 2 * ky * a1 * result0[i]; } } } } } // Take tensor product with interval to get prism for (std::size_t kz = 0; kz <= nderiv; ++kz) { for (std::size_t r = 1; r <= n; ++r) { const T a = 1.0 - 1.0 / static_cast(r); for (std::size_t kx = 0; kx <= nderiv - kz; ++kx) { for (std::size_t ky = 0; ky <= nderiv - kx - kz; ++ky) { auto result = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); auto result0 = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, idx(kx, ky, kz - 1), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t p = 0; p <= n; ++p) { for (std::size_t q = 0; q <= n - p; ++q) { for (std::size_t i = 0; i < result.extent(1); ++i) { result(prism_idx(p, q, r), i) = (x2[i] * 2.0 - 1.0) * result(prism_idx(p, q, r - 1), i) * (a + 1.0); } if (kz > 0) { for (std::size_t i = 0; i < result.extent(1); ++i) { result(prism_idx(p, q, r), i) += 2 * kz * result0(prism_idx(p, q, r - 1), i) * (a + 1.0); } } if (r > 1) { for (std::size_t i = 0; i < result.extent(1); ++i) { result(prism_idx(p, q, r), i) -= result(prism_idx(p, q, r - 2), i) * a; } } } } } } } } // Normalise for (std::size_t p = 0; p <= n; ++p) { for (std::size_t q = 0; q <= n - p; ++q) { for (std::size_t r = 0; r <= n; ++r) { auto pqr = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan( P, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent, prism_idx(p, q, r), MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent); for (std::size_t i = 0; i < pqr.extent(0); ++i) for (std::size_t j = 0; j < pqr.extent(1); ++j) pqr(i, j) *= std::sqrt((p + 0.5) * (p + q + 1) * (2 * r + 1)) * 2; } } } } } // namespace //----------------------------------------------------------------------------- template void polyset::tabulate( MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> P, cell::type celltype, polyset::type ptype, int d, int n, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> x) { switch (ptype) { case polyset::type::standard: switch (celltype) { case cell::type::point: tabulate_polyset_point_derivs(P, d, n, x); return; case cell::type::interval: tabulate_polyset_line_derivs(P, d, n, x); return; case cell::type::triangle: tabulate_polyset_triangle_derivs(P, d, n, x); return; case cell::type::tetrahedron: tabulate_polyset_tetrahedron_derivs(P, d, n, x); return; case cell::type::quadrilateral: tabulate_polyset_quad_derivs(P, d, n, x); return; case cell::type::prism: tabulate_polyset_prism_derivs(P, d, n, x); return; case cell::type::pyramid: tabulate_polyset_pyramid_derivs(P, d, n, x); return; case cell::type::hexahedron: tabulate_polyset_hex_derivs(P, d, n, x); return; default: throw std::runtime_error("Polynomial set: unsupported cell type"); } case polyset::type::macroedge: switch (celltype) { case cell::type::point: tabulate_polyset_point_derivs(P, d, n, x); return; case cell::type::interval: tabulate_polyset_line_macroedge_derivs(P, d, n, x); return; case cell::type::triangle: tabulate_polyset_triangle_macroedge_derivs(P, d, n, x); return; case cell::type::tetrahedron: tabulate_polyset_tetrahedron_macroedge_derivs(P, d, n, x); return; case cell::type::quadrilateral: tabulate_polyset_quadrilateral_macroedge_derivs(P, d, n, x); return; case cell::type::hexahedron: tabulate_polyset_hexahedron_macroedge_derivs(P, d, n, x); return; default: throw std::runtime_error("Polynomial set: unsupported cell type"); } default: throw std::runtime_error("Polynomial set: unsupported polynomial type."); } } //----------------------------------------------------------------------------- template std::pair, std::array> polyset::tabulate( cell::type celltype, polyset::type ptype, int d, int n, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> x) { std::array shape = {(std::size_t)polyset::nderivs(celltype, n), (std::size_t)polyset::dim(celltype, ptype, d), x.extent(0)}; std::vector P(shape[0] * shape[1] * shape[2]); MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> _P(P.data(), shape); polyset::tabulate(_P, celltype, ptype, d, n, x); return {std::move(P), std::move(shape)}; } //----------------------------------------------------------------------------- /// @cond template std::pair, std::array> polyset::tabulate( cell::type, polyset::type, int, int, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const float, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>); template std::pair, std::array> polyset::tabulate( cell::type, polyset::type, int, int, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const double, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>); /// @endcond //----------------------------------------------------------------------------- int polyset::dim(cell::type celltype, polyset::type ptype, int d) { switch (ptype) { case polyset::type::standard: switch (celltype) { case cell::type::point: return 1; case cell::type::triangle: return (d + 1) * (d + 2) / 2; case cell::type::tetrahedron: return (d + 1) * (d + 2) * (d + 3) / 6; case cell::type::prism: return (d + 1) * (d + 1) * (d + 2) / 2; case cell::type::pyramid: return (d + 1) * (d + 2) * (2 * d + 3) / 6; case cell::type::interval: return (d + 1); case cell::type::quadrilateral: return (d + 1) * (d + 1); case cell::type::hexahedron: return (d + 1) * (d + 1) * (d + 1); default: return 1; } case polyset::type::macroedge: switch (celltype) { case cell::type::point: return 1; case cell::type::interval: return 2 * d + 1; case cell::type::triangle: return (d + 1) * (2 * d + 1); case cell::type::tetrahedron: return (d + 1) * (2 * d + 1) * (2 * d + 3) / 3; case cell::type::quadrilateral: return (2 * d + 1) * (2 * d + 1); case cell::type::hexahedron: return (2 * d + 1) * (2 * d + 1) * (2 * d + 1); default: return 1; } default: return 1; } } //----------------------------------------------------------------------------- int polyset::nderivs(cell::type celltype, int n) { switch (celltype) { case cell::type::point: return 1; case cell::type::interval: return n + 1; case cell::type::triangle: return (n + 1) * (n + 2) / 2; case cell::type::quadrilateral: return (n + 1) * (n + 2) / 2; case cell::type::tetrahedron: return (n + 1) * (n + 2) * (n + 3) / 6; case cell::type::hexahedron: return (n + 1) * (n + 2) * (n + 3) / 6; case cell::type::prism: return (n + 1) * (n + 2) * (n + 3) / 6; case cell::type::pyramid: return (n + 1) * (n + 2) * (n + 3) / 6; default: return 1; } } //----------------------------------------------------------------------------- polyset::type polyset::superset(cell::type, polyset::type type1, polyset::type type2) { if (type1 == type2) return type1; if (type1 == polyset::type::standard) return type2; if (type2 == polyset::type::standard) return type1; throw std::runtime_error("Unsupported superset of polynomial sets."); } //----------------------------------------------------------------------------- polyset::type polyset::restriction(polyset::type ptype, cell::type cell, cell::type restriction_cell) { if (ptype == polyset::type::standard) return polyset::type::standard; if (cell == restriction_cell) return ptype; throw std::runtime_error("Unsupported restriction of polynomial sets."); } //----------------------------------------------------------------------------- fenics-basix-0.9.0/cpp/basix/polyset.h000066400000000000000000000257071470517546000176570ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson & Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #pragma once #include "cell.h" #include "mdspan.hpp" #include #include #include #include /// @brief Polynomial expansion sets. /// /// In Basix, the bases of the polynomial sets spanned by finite /// elements are represented by a set of coefficients of orthonormal /// polynomials on the cell. By orthogonality, we mean that /// /// \f[\int_R p_ip_j=\begin{cases}1&i=j\\0&i\not=j\end{cases}\f] /// /// ### Interval /// Legendre polynomials form a set of orthogonal polynomials on an /// interval. Legrendre polynomials are a special case of the Jacobi /// polynomials, \f$P_n^{\alpha, \beta}\f$, with the weights /// \f$(\alpha, \beta) = (0, 0)\f$. Using the Jacobi recurrence /// relation, we have /// /// \f[ n P^{0,0}_n(x) = (2n - 1) x P^{0,0}_{n - 1}(x) - (n-1) /// P^{0,0}_{n-2}(x),\f] /// /// starting with \f$P^{0,0}_0 = 1\f$. Legendre polynomials are /// orthogonal when integrated over the interval [-1, 1]. /// /// In Basix, the interval [0, 1] is used as the reference, so we apply /// the transformation \f$x\mapsto 2x-1\f$ to the standard Legendre /// polynomials. The orthogonal polynomials we use are therefore given /// by the recurrence relation /// /// \f[n p_n(x)=(2n-1)(2x-1)p_{n-1}(x)-(n-1)p_{n-2}(x).\f] /// /// For a given set of points, the recurrence relation can be used to /// compute the polynomial set up to arbitrary order. /// /// ### Triangle (Dubiner's basis) /// See Sherwin & /// Karniadakis (1995). /// /// An orthogonal set over a triangle can be obtained with a change of /// variable to \f$\zeta = 2\frac{1+x}{1 - y} - 1\f$, and a modified /// polynomial. The polynomial basis becomes: /// /// \f[ Q_{p,q} = P^{0,0}_p(\zeta) \left(\frac{1-y}{2}\right)^p\ P_q^{2p+1, /// 0}(y), \f] /// /// with a Legendre Polynomial of \f$\zeta\f$ and a weighted Jacobi /// Polynomial of \f$y\f$. In order to calculate these without actually /// multiplying together the two polynomials directly, we can first /// compute \f$Q_{p, 0}\f$ using a recurrence relation (since \f$P_0^{2p /// + 1, 0} = 1\f$): /// /// \f[ p Q_{p,0} = (2p-1)(2x + y+1)Q_{p-1,0} - (p-1)(1-y)^2 Q_{p-2, 0}.\f] /// /// Subsequently, we can calculate \f$Q_{p,q}\f$ by building up from /// \f$Q_{p,0}\f$ with another recurrence relation: /// /// \f[ Q_{p,q} = (a_0 y + a_1) Q_{p, q-1} + a_2 Q_{p, q-2}, \f] /// /// where \f$a_0, a_1, a_2\f$ are the coefficients in the Jacobi /// recurrence relation with \f$(\alpha,\beta) = (2p+1, 0)\f$, see Wikipedia. /// Note that \f$a_2 = 0\f$ when \f$q < 2\f$. /// /// ### Tetrahedron /// See Sherwin & /// Karniadakis (1995). /// /// Let \f$\zeta = 2\frac{1+x}{y+z} + 1\f$ and \f$\xi = 2\frac{1+y}{1-z} /// - 1\f$. Orthogonal polynomials on a tetrahedron are then given by /// /// \f[ Q_{p, q, r} = P^{0,0}_p(\zeta)\left(\frac{y+z}{2}\right)^p\ /// P_q^{2p+1,0}(\xi)\left(\frac{1-z}{2}\right)^q\ P_r^{2(p+q+1), 0}(z).\f] /// /// This can similarly be built up by first computing \f$Q_{p,0,0}\f$, /// then \f$Q_{p, q, 0}\f$ and finally \f$Q_{p, q, r}\f$ with recurrence /// relations on each axis. /// /// ### Quadrilateral and hexahedron /// The polynomial sets for quadrilateral and hexahedral elements can be /// formed by applying the recurrence relation for intervals in each /// direction. /// /// ### Prism /// The polynomial set for a prism can be computed by using using the /// recurrence relation for the triangle to get the polynomials that are /// constant with respect to \f$z\f$, then applying the recurrence /// relation for the interval to compute further polynomials with /// \f$z\f$s. /// /// ### Pyramid /// Orthogonal polynomials on the pyramid element are best calculated in /// the same way as the tetrahedron, using recurrence relations on each /// axis. Let \f$\zeta_x = 2\frac{1+x}{1-z} - 1\f$, \f$\zeta_y = /// 2\frac{1+y}{1-z} - 1\f$. The recurrence relation is then /// /// \f[Q_{p, q, r} = P^{0,0}_p(\zeta_x) P^{0,0}_q(\zeta_y) /// \left(\frac{1-z}{2}\right)^{(p+q)} P_r^{2(p+q+1), 0}(z).\f] /// /// ### Normalisation /// For each cell type, we obtain an orthonormal set of polynomials by /// scaling each of the orthogonal polynomials. /// /// ### Derivatives /// Recurrence relations can also be used to find the derivatives of the /// polynomials at given points. For example, on the interval, the first /// derivatives are given by /// /// \f[ n P'_n(x) = (2n - 1) \left(P_{n-1}(x) + x P'_{n - 1}(x)\right) + /// (n-1)P'_{n-2}(x). \f] /// /// More generally, the \f$k\f$-th derivative is given by /// /// \f[ n P^k_n(x) = (2n - 1) \left(k P^{k-1}_{n-1}(x) + x P^k_{n - /// 1}(x)\right)+ (n-1) P^k_{n-2}(x) \f] /// /// This is now a recurrence relation in both \f$n\f$ and \f$k\f$. /// Similar recurrence relations can be obtained for the derivatives of /// all the polynomial sets on the other shapes. Care must be taken with /// quadratic terms, and cross-terms in two and three dimensions. namespace basix::polyset { /// @brief Cell type enum class type { standard = 0, macroedge = 1, }; /// @brief Tabulate the orthonormal polynomial basis, and derivatives, /// at points on the reference cell. /// /// All derivatives up to the given order are computed. If derivatives /// are not required, use `n = 0`. For example, order `n = 2` for a 2D /// cell, will compute the basis \f$\psi, d\psi/dx, d\psi/dy, d^2 /// \psi/dx^2, d^2\psi/dxdy, d^2\psi/dy^2\f$ in that order (0, 0), (1, /// 0), (0, 1), (2, 0), (1, 1), (0 ,2). /// /// For an interval cell there are `nderiv + 1` derivatives, for a 2D /// cell, there are `(nderiv + 1)(nderiv + 2)/2` derivatives, and in 3D, /// there are `(nderiv + 1)(nderiv + 2)(nderiv + 3)/6`. The ordering is /// 'triangular' with the lower derivatives appearing first. /// /// @param[in] celltype Cell type /// @param[in] ptype The polynomial type /// @param[in] d Polynomial degree /// @param[in] n Maximum derivative order. Use n = 0 for the basis only. /// @param[in] x Points at which to evaluate the basis. The shape is /// (number of points, geometric dimension). /// @return Polynomial sets, for each derivative, tabulated at points. /// The shape is `(number of derivatives computed, number of points, /// basis index)`. /// /// - The first index is the derivative. The first entry is the basis /// itself. Derivatives are stored in triangular (2D) or tetrahedral /// (3D) ordering, eg if `(p, q)` denotes `p` order derivative with /// respect to `x` and `q` order derivative with respect to `y`, [0] -> /// (0, 0), [1] -> (1, 0), [2] -> (0, 1), [3] -> (2, 0), [4] -> (1, 1), /// [5] -> (0, 2), [6] -> (3, 0),... /// The function basix::indexing::idx maps tuples `(p, q, r)` to the /// array index. /// /// - The second index is the point, with index `i` corresponding to the /// point in row `i` of @p x. /// /// - The third index is the basis function index. /// @todo Does the order for the third index need to be documented? template std::pair, std::array> tabulate(cell::type celltype, polyset::type ptype, int d, int n, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> x); /// @brief Tabulate the orthonormal polynomial basis, and derivatives, /// at points on the reference cell. /// /// All derivatives up to the given order are computed. If derivatives /// are not required, use `n = 0`. For example, order `n = 2` for a 2D /// cell, will compute the basis \f$\psi, d\psi/dx, d\psi/dy, d^2 /// \psi/dx^2, d^2\psi/dxdy, d^2\psi/dy^2\f$ in that order (0, 0), (1, /// 0), (0, 1), (2, 0), (1, 1), (0 ,2). /// /// For an interval cell there are `nderiv + 1` derivatives, for a 2D /// cell, there are `(nderiv + 1)(nderiv + 2)/2` derivatives, and in 3D, /// there are `(nderiv + 1)(nderiv + 2)(nderiv + 3)/6`. The ordering is /// 'triangular' with the lower derivatives appearing first. /// /// @note This function will be called at runtime when tabulating a /// finite element, so its performance is critical. /// /// @param[in,out] P Polynomial sets, for each derivative, tabulated at /// points. The shape is `(number of derivatives computed, number of /// points, basis index)`. /// /// - The first index is the derivative. The first entry is the basis /// itself. Derivatives are stored in triangular (2D) or tetrahedral /// (3D) ordering, eg if `(p, q)` denotes `p` order derivative with /// respect to `x` and `q` order derivative with respect to `y`, [0] -> /// (0, 0), [1] -> (1, 0), [2] -> (0, 1), [3] -> (2, 0), [4] -> (1, 1), /// [5] -> (0, 2), [6] -> (3, 0),... /// The function basix::indexing::idx maps tuples `(p, q, r)` to the array /// index. /// /// - The second index is the point, with index `i` corresponding to the /// point in row `i` of `x`. /// /// - The third index is the basis function index. /// @todo Does the order for the third index need to be documented? /// @param[in] celltype Cell type /// @param[in] ptype The polynomial type /// @param[in] d Polynomial degree /// @param[in] n Maximum derivative order. Use `n=0` for the basis only. /// @param[in] x Points at which to evaluate the basis. The shape is /// `(number of points, geometric dimension)`. template void tabulate( MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> P, cell::type celltype, polyset::type ptype, int d, int n, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> x); /// @brief Dimension of a polynomial space /// @param[in] cell Cell type /// @param[in] ptype Polynomial type /// @param[in] d Polynomial degree /// @return The number of terms in the basis spanning a space of /// polynomial degree `d`. int dim(cell::type cell, polyset::type ptype, int d); /// @brief Number of derivatives that the orthonormal basis will have on /// the given cell. /// @param[in] cell Cell type /// @param[in] d Highest derivative order /// @return Number of derivatives int nderivs(cell::type cell, int d); /// @brief Get the polyset types that is a superset of two types on the /// given cell. /// @param[in] cell Cell type /// @param[in] type1 First polyset type /// @param[in] type2 Decond polyset type /// @return Superset type polyset::type superset(cell::type cell, polyset::type type1, polyset::type type2); /// @brief Get the polyset type that represents the restrictions of a /// type on a subentity. /// @param[in] ptype Polyset type /// @param[in] cell Cell type /// @param[in] restriction_cell Cell type of the subentity /// @return Restricted polyset type polyset::type restriction(polyset::type ptype, cell::type cell, cell::type restriction_cell); } // namespace basix::polyset fenics-basix-0.9.0/cpp/basix/precompute.cpp000066400000000000000000000007601470517546000206660ustar00rootroot00000000000000// Copyright (c) 2020 Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #include "precompute.h" using namespace basix; //----------------------------------------------------------------------------- void precompute::prepare_permutation(std::span perm) { for (std::size_t row = 0; row < perm.size(); ++row) { while (perm[row] < row) perm[row] = perm[perm[row]]; } } //----------------------------------------------------------------------------- fenics-basix-0.9.0/cpp/basix/precompute.h000066400000000000000000000251061470517546000203340ustar00rootroot00000000000000// Copyright (c) 2020 Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #pragma once #include "math.h" #include "mdspan.hpp" #include #include #include #include #include /// Matrix and permutation pre-computation namespace basix::precompute { namespace impl { /// @private These structs are used to get the float/value type from a /// template argument, including support for complex types. template struct scalar_value_type { /// @internal typedef T value_type; }; /// @private template struct scalar_value_type> { typedef typename T::value_type value_type; }; /// @private Convenience typedef template using scalar_value_type_t = typename scalar_value_type::value_type; } // namespace impl /// @brief Prepare a permutation. /// /// Computes representation of the permutation that allows the /// permutation to be applied without any temporary memory assignment. /// In pseudo code, this function does the following: /// /// \code{.pseudo} /// FOR index, entry IN perm: /// new_index = entry /// WHILE new_index < index: /// new_index = perm[new_index] /// OUT[index] = new_index /// \endcode /// /// Example /// ------- /// Consider the permutation `P = [1, 4, 0, 5, 2, 3]`. /// /// -# First, we look at the 0th entry. `P[0]` is 1. This is greater /// than 0, so the 0th entry of the output is 1. /// /// -# Next, we look at the 1st entry. `P[1]` is 4. This is greater than /// 1, so the 1st entry of the output is 4. /// /// -# Next, we look at the 2nd entry. `P[2]` is 0. This is less than 2, /// so we look at `P[0]. `P[0]` is 1. This is less than 2, so we look at /// `P[1]`. `P[1]` is 4. This is greater than 2, so the 2nd entry of the /// output is 4. /// /// -# Next, we look at the 3rd entry. `P[3]` is 5. This is greater than 3, /// so the 3rd entry of the output is 5. /// /// -# Next, we look at the 4th entry. `P[4]` is 2. This is less than 4, so /// we look at `P[2]`. `P[2]` is 0. This is less than 4, so we look at /// `P[0]`. `P[0]` is 1. This is less than 4, so we look at `P[1]`. /// `P[1]` is 4. This is greater than (or equal to) 4, so the 4th entry /// of the output is 4. /// /// -# Next, we look at the 5th entry. `P[5]` is 3. This is less than 5, /// so we look at `P[3]`. `P[3]` is 5. This is greater than (or equal /// to) 5, so the 5th entry of the output is 5. /// /// Hence, the output of this function in this case is `[1, 4, 4, 5, 4, /// 5]`. /// /// For an example of how the permutation in this form is applied, see /// apply_permutation(). /// /// @param[in,out] perm A permutation void prepare_permutation(std::span perm); /// @brief Apply a (precomputed) permutation \f$v = P u\f$. /// /// This uses the representation returned by prepare_permutation() to /// apply a permutation without needing any temporary memory. In pseudo /// code, this function does the following: /// /// \code{.pseudo} /// FOR index, entry IN perm: /// SWAP(data[index], data[entry]) /// \endcode /// /// If `n` is set, this will apply the permutation to every block. The /// `offset` is set, this will start applying the permutation at the /// `offset`th block. /// /// Example /// ------- /// Consider the permutation `P = [1, 4, 0, 5, 2, 3]`. In the /// documentation of prepare_permutation(), we saw that the precomputed /// representation of this permutation is `P2 = [1, 4, 4, 5, 4, 5]`. In /// this example, we look at how this representation can be used to /// apply this permutation to the array `A = [a, b, c, d, e, f]`. /// /// - `P2[0]` is 1, so we swap `A[0]` and `A[1]`. After this, `A = [b, /// a, c, d, e, f]`. /// /// - `P2[1]` is 4, so we swap `A[1]` and `A[4]`. After this, `A = [b, /// e, c, d, a, f]`. /// /// - `P2[2]` is 4, so we swap `A[2]` and `A[4]`. After this, `A = [b, e, /// a, d, c, f]`. /// /// - `P2[3]` is 5, so we swap `A[3]` and `A[5]`. After this, `A = [b, /// e, a, f, c, d]`. /// /// - `P2[4]` is 4, so we swap `A[4]` and `A[4]`. This changes nothing. /// /// - `P2[5]` is 5, so we swap `A[5]` and `A[5]`. This changes nothing. /// /// Therefore the result of applying this permutation is `[b, e, a, f, /// c, d]` (which is what we get if we apply the permutation directly). /// /// @note This function is designed to be called at runtime, so its /// performance is critical. /// /// @param[in] perm A permutation in precomputed form (as returned by /// prepare_permutation()). /// @param[in,out] data The data to apply the permutation to. It has /// shape `(m, n)` (uses row-major storage), where the permutation /// matrix has shape `(m, m)`. /// @param[in] offset The position in the data to start applying the /// permutation. /// @param[in] n The block size of the data. template void apply_permutation(std::span perm, std::span data, std::size_t offset = 0, std::size_t n = 1) { for (std::size_t i = 0; i < perm.size(); ++i) for (std::size_t b = 0; b < n; ++b) std::swap(data[n * (offset + i) + b], data[n * (offset + perm[i]) + b]); } /// @brief Permutation of mapped data. template void apply_permutation_mapped(std::span perm, std::span data, std::span emap, std::size_t n = 1) { for (std::size_t i = 0; i < perm.size(); ++i) for (std::size_t b = 0; b < n; ++b) std::swap(data[n * emap[i] + b], data[n * emap[perm[i]] + b]); } /// @brief Apply a (precomputed) permutation to some transposed data. /// /// Applies \f$v = u P^{T}\f$. /// /// @note This function is designed to be called at runtime, so its /// performance is critical. /// /// See apply_permutation(). template void apply_inv_permutation_right(std::span perm, std::span data, std::size_t offset = 0, std::size_t n = 1) { const std::size_t dim = perm.size(); const std::size_t data_size = (data.size() + (dim < n ? n - dim : 0)) / n; for (std::size_t b = 0; b < n; ++b) { for (std::size_t i = 0; i < dim; ++i) { std::swap(data[data_size * b + offset + i], data[data_size * b + offset + perm[i]]); } } } /// @brief Prepare a square matrix. /// /// Computes the LU decomposition of the transpose of the matrix. /// /// This function returns the permutation @f$P@f$P in the representation /// @f$PA^t=LU@f$ (precomputed as in prepare_permutation()). The LU /// decomposition of @f$A^t@f$ is computed in-place /// /// For an example of how the permutation in this form is applied, see /// apply_matrix(). /// /// @param[in,out] A The matrix data. /// @return The three parts of a precomputed representation of the /// matrix. These are (as described above): /// - A permutation (precomputed as in prepare_permutation()); /// - the vector @f$D@f$; template std::vector prepare_matrix(std::pair, std::array>& A) { return math::transpose_lu(A); } /// @brief Apply a (precomputed) matrix. /// /// This uses the representation returned by prepare_matrix() to apply a /// matrix without needing any temporary memory. In pseudo code, this /// function does the following: /// /// \code{.pseudo} /// INPUT perm, mat, data /// apply_permutation(perm, data) /// FOR i IN RANGE(dim): /// FOR j IN RANGE(i+1, dim): /// data[i] += mat[i, j] * data[j] /// FOR i IN RANGE(dim - 1, -1, -1): /// data[i] *= M[i, i] /// FOR j in RANGE(i): /// data[i] += mat[i, j] * data[j] /// \endcode /// /// If `n` is set, this will apply the permutation to every block. The /// `offset` is set, this will start applying the permutation at the /// `offset`th block. /// /// @note This function is designed to be called at runtime, so its /// performance is critical. /// /// @param[in] v_size_t A permutation, as computed by prepare_matrix(). /// @param[in] M The vector created by prepare_matrix(). /// @param[in,out] data The data to apply the permutation to /// @param[in] offset The position in the data to start applying the /// permutation /// @param[in] n The block size of the data template void apply_matrix( std::span v_size_t, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> M, std::span data, std::size_t offset = 0, std::size_t n = 1) { using U = typename impl::scalar_value_type_t; const std::size_t dim = v_size_t.size(); apply_permutation(v_size_t, data, offset, n); for (std::size_t b = 0; b < n; ++b) { for (std::size_t i = 0; i < dim; ++i) { for (std::size_t j = i + 1; j < dim; ++j) { data[n * (offset + i) + b] += static_cast(M(i, j)) * data[n * (offset + j) + b]; } } for (std::size_t i = 1; i <= dim; ++i) { data[n * (offset + dim - i) + b] *= static_cast(M(dim - i, dim - i)); for (std::size_t j = 0; j < dim - i; ++j) { data[n * (offset + dim - i) + b] += static_cast(M(dim - i, j)) * data[n * (offset + j) + b]; } } } } /// @brief Apply a (precomputed) matrix to some transposed data. /// /// Computes \f$v^{T} = M u^{T}\f$ (or equivalently \f$v = u M^{T}\f$). /// /// @note This function is designed to be called at runtime, so its /// performance is critical. /// /// See apply_matrix(). template void apply_tranpose_matrix_right( std::span v_size_t, MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> M, std::span data, std::size_t offset = 0, std::size_t n = 1) { using U = typename impl::scalar_value_type_t; const std::size_t dim = v_size_t.size(); const std::size_t data_size = (data.size() + (dim < n ? n - dim : 0)) / n; apply_inv_permutation_right(v_size_t, data, offset, n); for (std::size_t b = 0; b < n; ++b) { for (std::size_t i = 0; i < dim; ++i) { for (std::size_t j = i + 1; j < dim; ++j) { data[data_size * b + offset + i] += static_cast(M(i, j)) * data[data_size * b + offset + j]; } } for (std::size_t i = 1; i <= dim; ++i) { data[data_size * b + offset + dim - i] *= static_cast(M(dim - i, dim - i)); for (std::size_t j = 0; j < dim - i; ++j) { data[data_size * b + offset + dim - i] += static_cast(M(dim - i, j)) * data[data_size * b + offset + j]; } } } } } // namespace basix::precompute fenics-basix-0.9.0/cpp/basix/quadrature.cpp000066400000000000000000011674771470517546000207040ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson and Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #include "quadrature.h" #include "math.h" #include "mdspan.hpp" #include #include #include #include #include #include #include using namespace basix; namespace stdex = MDSPAN_IMPL_STANDARD_NAMESPACE::MDSPAN_IMPL_PROPOSED_NAMESPACE; template using mdspan_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>; template using mdarray_t = stdex::mdarray>; namespace { //---------------------------------------------------------------------------- /// Generate the recursion coefficients alpha_k, beta_k /// /// P_{k+1}(x) = (x-alpha_k)*P_{k}(x) - beta_k P_{k-1}(x) /// /// for the Jacobi polynomials which are orthogonal on [-1,1] /// with respect to the weight w(x)=[(1-x)^a]*[(1+x)^b] /// /// Adapted from the MATLAB code by Dirk Laurie and Walter Gautschi /// http://www.cs.purdue.edu/archives/2002/wxg/codes/r_jacobi.m /// /// @param[in] N polynomial order /// @param[in] a weight parameter /// @param[in] b weight parameter /// @returns (0) alpha and (1) beta recursion coefficients template std::array, 2> rec_jacobi(int N, T a, T b) { const T nu = (b - a) / (a + b + 2.0); const T mu = std::pow(2.0, (a + b + 1)) * std::tgamma(a + 1.0) * std::tgamma(b + 1.0) / std::tgamma(a + b + 2.0); std::vector n(N - 1); std::iota(n.begin(), n.end(), 1.0); std::vector nab(n.size()); std::ranges::transform(n, nab.begin(), [a, b](auto x) { return 2.0 * x + a + b; }); std::vector alpha(N); alpha.front() = nu; std::ranges::transform(nab, std::next(alpha.begin()), [a, b](auto nab) { return (b * b - a * a) / (nab * (nab + 2.0)); }); std::vector beta(N); beta.front() = mu; std::ranges::transform(n, nab, std::next(beta.begin()), [a, b](auto n, auto nab) { return 4 * (n + a) * (n + b) * n * (n + a + b) / (nab * nab * (nab + 1.0) * (nab - 1.0)); }); return {std::move(alpha), std::move(beta)}; } //---------------------------------------------------------------------------- /// @todo Add detail on alpha and beta /// @brief Compute Gauss points and weights on the domain [-1, 1] using /// the Golub-Welsch algorithm /// /// https://en.wikipedia.org/wiki/Gaussian_quadrature#The_Golub-Welsch_algorithm /// @param[in] alpha /// @param[in] beta /// @return (0) coordinates and (1) weights template std::array, 2> gauss(std::span alpha, std::span beta) { std::vector Abuffer(alpha.size() * alpha.size(), 0); mdspan_t A(Abuffer.data(), alpha.size(), alpha.size()); for (std::size_t i = 0; i < alpha.size(); ++i) A(i, i) = alpha[i]; for (std::size_t i = 0; i < alpha.size() - 1; ++i) { A(i + 1, i) = std::sqrt(beta[i + 1]); A(i, i + 1) = std::sqrt(beta[i + 1]); } auto [evals, evecs] = math::eigh(Abuffer, alpha.size()); // Determine weights from the first component of each eigenvector std::vector w(alpha.size()); for (std::size_t i = 0; i < alpha.size(); ++i) w[i] = beta[0] * evecs[i * alpha.size()] * evecs[i * alpha.size()]; return {std::move(evals), std::move(w)}; } //---------------------------------------------------------------------------- /// @brief Compute the Lobatto nodes and weights with the preassigned /// nodes xl1, xl2 /// /// Based on the section 7 of the paper "Some modified matrix eigenvalue /// problems", https://doi.org/10.1137/1015032. /// /// @param[in] alpha recursion coefficients /// @param[in] beta recursion coefficients /// @param[in] xl1 assigned node location /// @param[in] xl2 assigned node location /// @returns (0) quadrature positions and (1) quadrature weights. template std::array, 2> lobatto(std::span alpha, std::span beta, T xl1, T xl2) { assert(alpha.size() == beta.size()); // Solve tridiagonal system using Thomas algorithm T g1(0.0), g2(0.0); const std::size_t n = alpha.size(); for (std::size_t i = 1; i < n - 1; ++i) { g1 = std::sqrt(beta[i]) / (alpha[i] - xl1 - std::sqrt(beta[i - 1]) * g1); g2 = std::sqrt(beta[i]) / (alpha[i] - xl2 - std::sqrt(beta[i - 1]) * g2); } g1 = 1.0 / (alpha[n - 1] - xl1 - std::sqrt(beta[n - 2]) * g1); g2 = 1.0 / (alpha[n - 1] - xl2 - std::sqrt(beta[n - 2]) * g2); std::vector alpha_l(alpha.begin(), alpha.end()); alpha_l[n - 1] = (g1 * xl2 - g2 * xl1) / (g1 - g2); std::vector beta_l(beta.begin(), beta.end()); beta_l[n - 1] = (xl2 - xl1) / (g1 - g2); return gauss(std::span(alpha_l), std::span(beta_l)); } //----------------------------------------------------------------------------- /// Evaluate the nth Jacobi polynomial and derivatives with weight /// parameters (a, 0) at points x /// @param[in] a Jacobi weight a /// @param[in] n Order of polynomial /// @param[in] nderiv Number of derivatives (if zero, just compute /// polynomial itself) /// @param[in] x Points at which to evaluate /// @return Array of polynomial derivative values (rows) at points /// (columns) template mdarray_t compute_jacobi_deriv(T a, std::size_t n, std::size_t nderiv, std::span x) { std::vector shape = {x.size()}; mdarray_t J(nderiv + 1, n + 1, x.size()); mdarray_t Jd(n + 1, x.size()); for (std::size_t i = 0; i < nderiv + 1; ++i) { if (i == 0) { for (std::size_t j = 0; j < Jd.extent(1); ++j) Jd(0, j) = 1.0; } else { for (std::size_t j = 0; j < Jd.extent(1); ++j) Jd(0, j) = 0.0; } if (n > 0) { if (i == 0) { for (std::size_t j = 0; j < Jd.extent(1); ++j) Jd(1, j) = (x[j] * (a + 2.0) + a) * 0.5; } else if (i == 1) { for (std::size_t j = 0; j < Jd.extent(1); ++j) Jd(1, j) = a * 0.5 + 1; } else { for (std::size_t j = 0; j < Jd.extent(1); ++j) Jd(1, j) = 0.0; } } for (std::size_t j = 2; j < n + 1; ++j) { const T a1 = 2 * j * (j + a) * (2 * j + a - 2); const T a2 = (2 * j + a - 1) * (a * a) / a1; const T a3 = (2 * j + a - 1) * (2 * j + a) / (2 * j * (j + a)); const T a4 = 2 * (j + a - 1) * (j - 1) * (2 * j + a) / a1; for (std::size_t k = 0; k < Jd.extent(1); ++k) Jd(j, k) = Jd(j - 1, k) * (x[k] * a3 + a2) - Jd(j - 2, k) * a4; if (i > 0) { for (std::size_t k = 0; k < Jd.extent(1); ++k) Jd(j, k) += i * a3 * J(i - 1, j - 1, k); } } for (std::size_t j = 0; j < Jd.extent(0); ++j) for (std::size_t k = 0; k < Jd.extent(1); ++k) J(i, j, k) = Jd(j, k); } mdarray_t result(nderiv + 1, x.size()); for (std::size_t i = 0; i < result.extent(0); ++i) for (std::size_t j = 0; j < result.extent(1); ++j) result(i, j) = J(i, n, j); return result; } //---------------------------------------------------------------------------- /// Computes the m roots of \f$P_{m}^{a,0}\f$ on [-1,1] by Newton's /// method. The initial guesses are the Chebyshev points. Algorithm /// implemented from the pseudocode given by Karniadakis and Sherwin. template std::vector compute_gauss_jacobi_points(T a, int m) { constexpr T eps = 1.0e-8; constexpr int max_iter = 100; std::vector x(m); for (int k = 0; k < m; ++k) { // Initial guess x[k] = -std::cos((2.0 * k + 1.0) * M_PI / (2.0 * m)); if (k > 0) x[k] = 0.5 * (x[k] + x[k - 1]); int j = 0; while (j < max_iter) { T s = 0; for (int i = 0; i < k; ++i) s += 1.0 / (x[k] - x[i]); std::span _x(&x[k], 1); mdarray_t f = compute_jacobi_deriv(a, m, 1, _x); T delta = f(0, 0) / (f(1, 0) - f(0, 0) * s); x[k] -= delta; if (std::abs(delta) < eps) break; ++j; } } return x; } //----------------------------------------------------------------------------- /// @note Computes on [-1, 1] template std::array, 2> compute_gauss_jacobi_rule(T a, int m) { std::vector pts = compute_gauss_jacobi_points(a, m); mdarray_t Jd = compute_jacobi_deriv(a, m, 1, pts); T a1 = std::pow(2.0, a + 1.0); std::vector wts(m); for (int i = 0; i < m; ++i) { T x = pts[i]; T f = Jd(1, i); wts[i] = a1 / (1.0 - x * x) / (f * f); } return {std::move(pts), std::move(wts)}; } //----------------------------------------------------------------------------- template std::array, 2> make_quadrature_line(int m) { auto [ptx, wx] = compute_gauss_jacobi_rule(0.0, m); std::ranges::transform(wx, wx.begin(), [](auto w) { return 0.5 * w; }); std::ranges::transform(ptx, ptx.begin(), [](auto x) { return 0.5 * (x + 1.0); }); return {std::move(ptx), std::move(wx)}; } //----------------------------------------------------------------------------- template std::array, 2> make_quadrature_triangle_collapsed(std::size_t m) { auto [ptx, wx] = compute_gauss_jacobi_rule(0.0, m); auto [pty, wy] = compute_gauss_jacobi_rule(1.0, m); std::vector pts(m * m * 2); mdspan_t x(pts.data(), m * m, 2); std::vector wts(m * m); int c = 0; for (std::size_t i = 0; i < m; ++i) { for (std::size_t j = 0; j < m; ++j) { x(c, 0) = 0.25 * (1.0 + ptx[i]) * (1.0 - pty[j]); x(c, 1) = 0.5 * (1.0 + pty[j]); wts[c] = wx[i] * wy[j] * 0.125; ++c; } } return {std::move(pts), std::move(wts)}; } //----------------------------------------------------------------------------- template std::array, 2> make_quadrature_tetrahedron_collapsed(std::size_t m) { auto [ptx, wx] = compute_gauss_jacobi_rule(0.0, m); auto [pty, wy] = compute_gauss_jacobi_rule(1.0, m); auto [ptz, wz] = compute_gauss_jacobi_rule(2.0, m); std::vector pts(m * m * m * 3); mdspan_t x(pts.data(), m * m * m, 3); std::vector wts(m * m * m); int c = 0; for (std::size_t i = 0; i < m; ++i) { for (std::size_t j = 0; j < m; ++j) { for (std::size_t k = 0; k < m; ++k) { x(c, 0) = 0.125 * (1.0 + ptx[i]) * (1.0 - pty[j]) * (1.0 - ptz[k]); x(c, 1) = 0.25 * (1. + pty[j]) * (1. - ptz[k]); x(c, 2) = 0.5 * (1.0 + ptz[k]); wts[c] = wx[i] * wy[j] * wz[k] * 0.125 * 0.125; ++c; } } } return {std::move(pts), std::move(wts)}; } //----------------------------------------------------------------------------- template std::array, 2> make_gauss_jacobi_quadrature(cell::type celltype, std::size_t m) { const std::size_t np = (m + 2) / 2; switch (celltype) { case cell::type::interval: return make_quadrature_line(np); case cell::type::quadrilateral: { auto [QptsL, QwtsL] = make_quadrature_line(np); std::vector pts(np * np * 2); mdspan_t x(pts.data(), np * np, 2); std::vector wts(np * np); int c = 0; for (std::size_t i = 0; i < np; ++i) { for (std::size_t j = 0; j < np; ++j) { x(c, 0) = QptsL[i]; x(c, 1) = QptsL[j]; wts[c] = QwtsL[i] * QwtsL[j]; ++c; } } return {std::move(pts), std::move(wts)}; } case cell::type::hexahedron: { auto [QptsL, QwtsL] = make_quadrature_line(np); std::vector pts(np * np * np * 3); mdspan_t x(pts.data(), np * np * np, 3); std::vector wts(np * np * np); int c = 0; for (std::size_t i = 0; i < np; ++i) { for (std::size_t j = 0; j < np; ++j) { for (std::size_t k = 0; k < np; ++k) { x(c, 0) = QptsL[i]; x(c, 1) = QptsL[j]; x(c, 2) = QptsL[k]; wts[c] = QwtsL[i] * QwtsL[j] * QwtsL[k]; ++c; } } } return {std::move(pts), std::move(wts)}; } case cell::type::prism: { const auto [QptsL, QwtsL] = make_quadrature_line(np); const auto [_QptsT, QwtsT] = make_quadrature_triangle_collapsed(np); mdspan_t QptsT(_QptsT.data(), QwtsT.size(), _QptsT.size() / QwtsT.size()); std::vector pts(np * QptsT.extent(0) * 3); mdspan_t x(pts.data(), np * QptsT.extent(0), 3); std::vector wts(np * QptsT.extent(0)); int c = 0; for (std::size_t i = 0; i < QptsT.extent(0); ++i) { for (std::size_t k = 0; k < np; ++k) { x(c, 0) = QptsT(i, 0); x(c, 1) = QptsT(i, 1); x(c, 2) = QptsL[k]; wts[c] = QwtsT[i] * QwtsL[k]; ++c; } } return {std::move(pts), std::move(wts)}; } case cell::type::pyramid: { auto [pts, wts] = make_gauss_jacobi_quadrature(cell::type::hexahedron, m + 2); mdspan_t x(pts.data(), pts.size() / 3, 3); for (std::size_t i = 0; i < x.extent(0); ++i) { const auto z = x(i, 2); x(i, 0) *= (1 - z); x(i, 1) *= (1 - z); wts[i] *= (1 - z) * (1 - z); } return {std::move(pts), std::move(wts)}; } case cell::type::triangle: return make_quadrature_triangle_collapsed(np); case cell::type::tetrahedron: return make_quadrature_tetrahedron_collapsed(np); default: throw std::runtime_error("Unsupported celltype for make_quadrature"); } } //----------------------------------------------------------------------------- /// The Gauss-Lobatto-Legendre quadrature rules on the interval using /// Greg von Winckel's implementation. This facilitates implementing /// spectral elements. The quadrature rule uses m points for a degree of /// precision of 2m-3. template std::array, 2> compute_gll_rule(int m) { if (m < 2) { throw std::runtime_error( "Gauss-Lobatto-Legendre quadrature invalid for fewer than 2 points"); } // Calculate the recursion coefficients // auto [alpha, beta] = rec_jacobi(m, 0.0, 0.0); std::array, 2> coeffs = rec_jacobi(m, 0.0, 0.0); // Compute Lobatto nodes and weights auto [xs_ref, ws_ref] = lobatto(std::span(coeffs[0]), std::span(coeffs[1]), -1.0, 1.0); // Reorder to match 1d dof ordering std::rotate(xs_ref.rbegin(), xs_ref.rbegin() + 1, xs_ref.rend() - 1); std::rotate(ws_ref.rbegin(), ws_ref.rbegin() + 1, ws_ref.rend() - 1); return {xs_ref, ws_ref}; } //----------------------------------------------------------------------------- template std::array, 2> make_gll_line(int m) { auto [ptx, wx] = compute_gll_rule(m); std::ranges::transform(wx, wx.begin(), [](auto w) { return 0.5 * w; }); std::ranges::transform(ptx, ptx.begin(), [](auto x) { return 0.5 * (x + 1.0); }); return {ptx, wx}; } //----------------------------------------------------------------------------- template std::array, 2> make_gll_quadrature(cell::type celltype, std::size_t m) { const std::size_t np = (m + 4) / 2; switch (celltype) { case cell::type::interval: return make_gll_line(np); case cell::type::quadrilateral: { auto [QptsL, QwtsL] = make_gll_line(np); std::vector pts(np * np * 2); mdspan_t x(pts.data(), np * np, 2); std::vector wts(np * np); int c = 0; for (std::size_t i = 0; i < np; ++i) { for (std::size_t j = 0; j < np; ++j) { x(c, 0) = QptsL[i]; x(c, 1) = QptsL[j]; wts[c] = QwtsL[i] * QwtsL[j]; ++c; } } return {std::move(pts), std::move(wts)}; } case cell::type::hexahedron: { auto [QptsL, QwtsL] = make_gll_line(np); std::vector pts(np * np * np * 3); mdspan_t x(pts.data(), np * np * np, 3); std::vector wts(np * np * np); int c = 0; for (std::size_t i = 0; i < np; ++i) { for (std::size_t j = 0; j < np; ++j) { for (std::size_t k = 0; k < np; ++k) { x(c, 0) = QptsL[i]; x(c, 1) = QptsL[j]; x(c, 2) = QptsL[k]; wts[c] = QwtsL[i] * QwtsL[j] * QwtsL[k]; ++c; } } } return {std::move(pts), std::move(wts)}; } case cell::type::prism: throw std::runtime_error("Prism not yet supported"); case cell::type::pyramid: throw std::runtime_error("Pyramid not yet supported"); case cell::type::triangle: throw std::runtime_error("Triangle not yet supported"); case cell::type::tetrahedron: throw std::runtime_error("Tetrahedron not yet supported"); default: throw std::runtime_error("Unsupported celltype for make_quadrature"); } } //----------------------------------------------------------------------------- template std::array, 2> make_strang_fix_quadrature(cell::type celltype, std::size_t m) { if (celltype == cell::type::triangle) { if (m == 2) { // Scheme from Strang and Fix, 3 points, degree of precision 2 std::vector x = {1.0 / 6.0, 1.0 / 6.0, 1.0 / 6.0, 2.0 / 3.0, 2.0 / 3.0, 1.0 / 6.0}; std::vector w = {1.0 / 6.0, 1.0 / 6.0, 1.0 / 6.0}; return {std::move(x), std::move(w)}; } else if (m == 3) { // Scheme from Strang and Fix, 6 points, degree of precision 3 std::vector x = {0.659027622374092, 0.231933368553031, 0.659027622374092, 0.109039009072877, 0.231933368553031, 0.659027622374092, 0.231933368553031, 0.109039009072877, 0.109039009072877, 0.659027622374092, 0.109039009072877, 0.231933368553031}; std::vector w(6, 1.0 / 12.0); return {std::move(x), std::move(w)}; } else if (m == 4) { // Scheme from Strang and Fix, 6 points, degree of precision 4 std::vector x = {0.816847572980459, 0.091576213509771, 0.091576213509771, 0.816847572980459, 0.091576213509771, 0.091576213509771, 0.108103018168070, 0.445948490915965, 0.445948490915965, 0.108103018168070, 0.445948490915965, 0.445948490915965}; std::vector w = {0.054975871827661, 0.054975871827661, 0.054975871827661, 0.1116907948390055, 0.1116907948390055, 0.1116907948390055}; return {std::move(x), std::move(w)}; } else if (m == 5) { // Scheme from Strang and Fix, 7 points, degree of precision 5 std::vector x = {0.33333333333333333, 0.33333333333333333, 0.79742698535308720, 0.10128650732345633, 0.10128650732345633, 0.79742698535308720, 0.10128650732345633, 0.10128650732345633, 0.05971587178976981, 0.47014206410511505, 0.47014206410511505, 0.05971587178976981, 0.47014206410511505, 0.47014206410511505}; std::vector w = {0.1125, 0.06296959027241358, 0.06296959027241358, 0.06296959027241358, 0.06619707639425308, 0.06619707639425308, 0.06619707639425308}; return {std::move(x), std::move(w)}; } else if (m == 6) { // Scheme from Strang and Fix, 12 points, degree of precision 6 std::vector x = {0.873821971016996, 0.063089014491502, 0.063089014491502, 0.873821971016996, 0.063089014491502, 0.063089014491502, 0.501426509658179, 0.249286745170910, 0.249286745170910, 0.501426509658179, 0.249286745170910, 0.249286745170910, 0.636502499121399, 0.310352451033785, 0.636502499121399, 0.053145049844816, 0.310352451033785, 0.636502499121399, 0.310352451033785, 0.053145049844816, 0.053145049844816, 0.636502499121399, 0.053145049844816, 0.310352451033785}; std::vector w = {0.0254224531851035, 0.0254224531851035, 0.0254224531851035, 0.0583931378631895, 0.0583931378631895, 0.0583931378631895, 0.041425537809187, 0.041425537809187, 0.041425537809187, 0.041425537809187, 0.041425537809187, 0.041425537809187}; return {std::move(x), std::move(w)}; } else throw std::runtime_error("Strang-Fix not implemented for this order."); } throw std::runtime_error("Strang-Fix not implemented for this cell type."); } //------------------------------------------------------------------------------------------------------- template std::array, 2> make_zienkiewicz_taylor_quadrature(cell::type celltype, std::size_t m) { if (celltype == cell::type::triangle) { if (m == 0 or m == 1) { // Scheme from Zienkiewicz and Taylor, 1 point, degree of precision 1 return {std::vector{1.0 / 3.0, 1.0 / 3.0}, {0.5}}; } else { throw std::runtime_error( "Zienkiewicz-Taylor not implemented for this order."); } } if (celltype == cell::type::tetrahedron) { if (m == 0 or m == 1) { // Scheme from Zienkiewicz and Taylor, 1 point, degree of precision 1 return {std::vector{0.25, 0.25, 0.25}, {1.0 / 6.0}}; } else if (m == 2) { // Scheme from Zienkiewicz and Taylor, 4 points, degree of // precision // 2 constexpr T a = 0.585410196624969, b = 0.138196601125011; std::vector x = {a, b, b, b, a, b, b, b, a, b, b, b}; return {std::move(x), {1.0 / 24.0, 1.0 / 24.0, 1.0 / 24.0, 1.0 / 24.0}}; } else if (m == 3) { // Scheme from Zienkiewicz and Taylor, 5 points, degree of // precision 3 Note : this scheme has a negative weight std::vector x{ 0.2500000000000000, 0.2500000000000000, 0.2500000000000000, 0.5000000000000000, 0.1666666666666666, 0.1666666666666666, 0.1666666666666666, 0.5000000000000000, 0.1666666666666666, 0.1666666666666666, 0.1666666666666666, 0.5000000000000000, 0.1666666666666666, 0.1666666666666666, 0.1666666666666666}; std::vector w{-0.8 / 6.0, 0.45 / 6.0, 0.45 / 6.0, 0.45 / 6.0, 0.45 / 6.0}; return {std::move(x), std::move(w)}; } else { throw std::runtime_error( "Zienkiewicz-Taylor not implemented for this order."); } } throw std::runtime_error( "Zienkiewicz-Taylor not implemented for this cell type."); } //----------------------------------------------------------------------------- template std::array, 2> make_keast_quadrature(cell::type celltype, std::size_t m) { if (celltype == cell::type::tetrahedron) { if (m == 4) { // Keast rule, 14 points, degree of precision 4 // Values taken from // http://people.sc.fsu.edu/~jburkardt/datasets/quadrature_rules_tet/quadrature_rules_tet.html // (KEAST5) std::vector x = {0.0000000000000000, 0.5000000000000000, 0.5000000000000000, 0.5000000000000000, 0.0000000000000000, 0.5000000000000000, 0.5000000000000000, 0.5000000000000000, 0.0000000000000000, 0.5000000000000000, 0.0000000000000000, 0.0000000000000000, 0.0000000000000000, 0.5000000000000000, 0.0000000000000000, 0.0000000000000000, 0.0000000000000000, 0.5000000000000000, 0.6984197043243866, 0.1005267652252045, 0.1005267652252045, 0.1005267652252045, 0.1005267652252045, 0.1005267652252045, 0.1005267652252045, 0.1005267652252045, 0.6984197043243866, 0.1005267652252045, 0.6984197043243866, 0.1005267652252045, 0.0568813795204234, 0.3143728734931922, 0.3143728734931922, 0.3143728734931922, 0.3143728734931922, 0.3143728734931922, 0.3143728734931922, 0.3143728734931922, 0.0568813795204234, 0.3143728734931922, 0.0568813795204234, 0.3143728734931922}; std::vector w = {0.003174603174603167, 0.003174603174603167, 0.003174603174603167, 0.003174603174603167, 0.003174603174603167, 0.003174603174603167, 0.014764970790496783, 0.014764970790496783, 0.014764970790496783, 0.014764970790496783, 0.022139791114265117, 0.022139791114265117, 0.022139791114265117, 0.022139791114265117}; return {std::move(x), std::move(w)}; } else if (m == 5) { // Keast rule, 15 points, degree of precision 5 // Values taken from // http://people.sc.fsu.edu/~jburkardt/datasets/quadrature_rules_tet/quadrature_rules_tet.html // (KEAST6) std::vector x = {0.2500000000000000, 0.2500000000000000, 0.2500000000000000, 0.0000000000000000, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.0000000000000000, 0.3333333333333333, 0.0000000000000000, 0.3333333333333333, 0.7272727272727273, 0.0909090909090909, 0.0909090909090909, 0.0909090909090909, 0.0909090909090909, 0.0909090909090909, 0.0909090909090909, 0.0909090909090909, 0.7272727272727273, 0.0909090909090909, 0.7272727272727273, 0.0909090909090909, 0.4334498464263357, 0.0665501535736643, 0.0665501535736643, 0.0665501535736643, 0.4334498464263357, 0.0665501535736643, 0.0665501535736643, 0.0665501535736643, 0.4334498464263357, 0.0665501535736643, 0.4334498464263357, 0.4334498464263357, 0.4334498464263357, 0.0665501535736643, 0.4334498464263357, 0.4334498464263357, 0.4334498464263357, 0.0665501535736643}; std::vector w = {0.030283678097089182, 0.006026785714285717, 0.006026785714285717, 0.006026785714285717, 0.006026785714285717, 0.011645249086028967, 0.011645249086028967, 0.011645249086028967, 0.011645249086028967, 0.010949141561386449, 0.010949141561386449, 0.010949141561386449, 0.010949141561386449, 0.010949141561386449, 0.010949141561386449}; return {std::move(x), std::move(w)}; } else if (m == 6) { // Keast rule, 24 points, degree of precision 6 // Values taken from // http://people.sc.fsu.edu/~jburkardt/datasets/quadrature_rules_tet/quadrature_rules_tet.html // (KEAST7) std::vector x = {0.3561913862225449, 0.2146028712591517, 0.2146028712591517, 0.2146028712591517, 0.2146028712591517, 0.2146028712591517, 0.2146028712591517, 0.2146028712591517, 0.3561913862225449, 0.2146028712591517, 0.3561913862225449, 0.2146028712591517, 0.8779781243961660, 0.0406739585346113, 0.0406739585346113, 0.0406739585346113, 0.0406739585346113, 0.0406739585346113, 0.0406739585346113, 0.0406739585346113, 0.8779781243961660, 0.0406739585346113, 0.8779781243961660, 0.0406739585346113, 0.0329863295731731, 0.3223378901422757, 0.3223378901422757, 0.3223378901422757, 0.3223378901422757, 0.3223378901422757, 0.3223378901422757, 0.3223378901422757, 0.0329863295731731, 0.3223378901422757, 0.0329863295731731, 0.3223378901422757, 0.2696723314583159, 0.0636610018750175, 0.0636610018750175, 0.0636610018750175, 0.2696723314583159, 0.0636610018750175, 0.0636610018750175, 0.0636610018750175, 0.2696723314583159, 0.6030056647916491, 0.0636610018750175, 0.0636610018750175, 0.0636610018750175, 0.6030056647916491, 0.0636610018750175, 0.0636610018750175, 0.0636610018750175, 0.6030056647916491, 0.0636610018750175, 0.2696723314583159, 0.6030056647916491, 0.2696723314583159, 0.6030056647916491, 0.0636610018750175, 0.6030056647916491, 0.0636610018750175, 0.2696723314583159, 0.0636610018750175, 0.6030056647916491, 0.2696723314583159, 0.2696723314583159, 0.0636610018750175, 0.6030056647916491, 0.6030056647916491, 0.2696723314583159, 0.0636610018750175}; std::vector w = { 0.0066537917096946494, 0.0066537917096946494, 0.0066537917096946494, 0.0066537917096946494, 0.0016795351758867834, 0.0016795351758867834, 0.0016795351758867834, 0.0016795351758867834, 0.009226196923942399, 0.009226196923942399, 0.009226196923942399, 0.009226196923942399, 0.008035714285714283, 0.008035714285714283, 0.008035714285714283, 0.008035714285714283, 0.008035714285714283, 0.008035714285714283, 0.008035714285714283, 0.008035714285714283, 0.008035714285714283, 0.008035714285714283, 0.008035714285714283, 0.008035714285714283}; return {std::move(x), std::move(w)}; } else if (m == 7) { // Keast rule, 31 points, degree of precision 7 // Values taken from // http://people.sc.fsu.edu/~jburkardt/datasets/quadrature_rules_tet/quadrature_rules_tet.html // (KEAST8) std::vector x = {0.2500000000000000, 0.2500000000000000, 0.2500000000000000, 0.7653604230090441, 0.0782131923303186, 0.0782131923303186, 0.0782131923303186, 0.0782131923303186, 0.0782131923303186, 0.0782131923303186, 0.0782131923303186, 0.7653604230090441, 0.0782131923303186, 0.7653604230090441, 0.0782131923303186, 0.6344703500082868, 0.1218432166639044, 0.1218432166639044, 0.1218432166639044, 0.1218432166639044, 0.1218432166639044, 0.1218432166639044, 0.1218432166639044, 0.6344703500082868, 0.1218432166639044, 0.6344703500082868, 0.1218432166639044, 0.0023825066607383, 0.3325391644464206, 0.3325391644464206, 0.3325391644464206, 0.3325391644464206, 0.3325391644464206, 0.3325391644464206, 0.3325391644464206, 0.0023825066607383, 0.3325391644464206, 0.0023825066607383, 0.3325391644464206, 0.0000000000000000, 0.5000000000000000, 0.5000000000000000, 0.5000000000000000, 0.0000000000000000, 0.5000000000000000, 0.5000000000000000, 0.5000000000000000, 0.0000000000000000, 0.5000000000000000, 0.0000000000000000, 0.0000000000000000, 0.0000000000000000, 0.5000000000000000, 0.0000000000000000, 0.0000000000000000, 0.0000000000000000, 0.5000000000000000, 0.2000000000000000, 0.1000000000000000, 0.1000000000000000, 0.1000000000000000, 0.2000000000000000, 0.1000000000000000, 0.1000000000000000, 0.1000000000000000, 0.2000000000000000, 0.6000000000000000, 0.1000000000000000, 0.1000000000000000, 0.1000000000000000, 0.6000000000000000, 0.1000000000000000, 0.1000000000000000, 0.1000000000000000, 0.6000000000000000, 0.1000000000000000, 0.2000000000000000, 0.6000000000000000, 0.2000000000000000, 0.6000000000000000, 0.1000000000000000, 0.6000000000000000, 0.1000000000000000, 0.2000000000000000, 0.1000000000000000, 0.6000000000000000, 0.2000000000000000, 0.2000000000000000, 0.1000000000000000, 0.6000000000000000, 0.6000000000000000, 0.2000000000000000, 0.1000000000000000}; std::vector w = {0.0182642234661088, 0.010599941524414166, 0.010599941524414166, 0.010599941524414166, 0.010599941524414166, -0.06251774011432995, -0.06251774011432995, -0.06251774011432995, -0.06251774011432995, 0.004891425263073534, 0.004891425263073534, 0.004891425263073534, 0.004891425263073534, 0.0009700176366843, 0.0009700176366843, 0.0009700176366843, 0.0009700176366843, 0.0009700176366843, 0.0009700176366843, 0.02755731922398508, 0.02755731922398508, 0.02755731922398508, 0.02755731922398508, 0.02755731922398508, 0.02755731922398508, 0.02755731922398508, 0.02755731922398508, 0.02755731922398508, 0.02755731922398508, 0.02755731922398508, 0.02755731922398508}; return {std::move(x), std::move(w)}; } else if (m == 8) { // Keast rule, 45 points, degree of precision 8 // Values taken from // http://people.sc.fsu.edu/~jburkardt/datasets/quadrature_rules_tet/quadrature_rules_tet.html // (KEAST9) std::vector x = {0.2500000000000000, 0.2500000000000000, 0.2500000000000000, 0.6175871903000830, 0.1274709365666390, 0.1274709365666390, 0.1274709365666390, 0.1274709365666390, 0.1274709365666390, 0.1274709365666390, 0.1274709365666390, 0.6175871903000830, 0.1274709365666390, 0.6175871903000830, 0.1274709365666390, 0.9037635088221031, 0.0320788303926323, 0.0320788303926323, 0.0320788303926323, 0.0320788303926323, 0.0320788303926323, 0.0320788303926323, 0.0320788303926323, 0.9037635088221031, 0.0320788303926323, 0.9037635088221031, 0.0320788303926323, 0.4502229043567190, 0.0497770956432810, 0.0497770956432810, 0.0497770956432810, 0.4502229043567190, 0.0497770956432810, 0.0497770956432810, 0.0497770956432810, 0.4502229043567190, 0.0497770956432810, 0.4502229043567190, 0.4502229043567190, 0.4502229043567190, 0.0497770956432810, 0.4502229043567190, 0.4502229043567190, 0.4502229043567190, 0.0497770956432810, 0.3162695526014501, 0.1837304473985499, 0.1837304473985499, 0.1837304473985499, 0.3162695526014501, 0.1837304473985499, 0.1837304473985499, 0.1837304473985499, 0.3162695526014501, 0.1837304473985499, 0.3162695526014501, 0.3162695526014501, 0.3162695526014501, 0.1837304473985499, 0.3162695526014501, 0.3162695526014501, 0.3162695526014501, 0.1837304473985499, 0.0229177878448171, 0.2319010893971509, 0.2319010893971509, 0.2319010893971509, 0.0229177878448171, 0.2319010893971509, 0.2319010893971509, 0.2319010893971509, 0.0229177878448171, 0.5132800333608811, 0.2319010893971509, 0.2319010893971509, 0.2319010893971509, 0.5132800333608811, 0.2319010893971509, 0.2319010893971509, 0.2319010893971509, 0.5132800333608811, 0.2319010893971509, 0.0229177878448171, 0.5132800333608811, 0.0229177878448171, 0.5132800333608811, 0.2319010893971509, 0.5132800333608811, 0.2319010893971509, 0.0229177878448171, 0.2319010893971509, 0.5132800333608811, 0.0229177878448171, 0.0229177878448171, 0.2319010893971509, 0.5132800333608811, 0.5132800333608811, 0.0229177878448171, 0.2319010893971509, 0.7303134278075384, 0.0379700484718286, 0.0379700484718286, 0.0379700484718286, 0.7303134278075384, 0.0379700484718286, 0.0379700484718286, 0.0379700484718286, 0.7303134278075384, 0.1937464752488044, 0.0379700484718286, 0.0379700484718286, 0.0379700484718286, 0.1937464752488044, 0.0379700484718286, 0.0379700484718286, 0.0379700484718286, 0.1937464752488044, 0.0379700484718286, 0.7303134278075384, 0.1937464752488044, 0.7303134278075384, 0.1937464752488044, 0.0379700484718286, 0.1937464752488044, 0.0379700484718286, 0.7303134278075384, 0.0379700484718286, 0.1937464752488044, 0.7303134278075384, 0.7303134278075384, 0.0379700484718286, 0.1937464752488044, 0.1937464752488044, 0.7303134278075384, 0.0379700484718286}; std::vector w = { -0.03932700664129262, 0.0040813160593427, 0.0040813160593427, 0.0040813160593427, 0.0040813160593427, 0.0006580867733043499, 0.0006580867733043499, 0.0006580867733043499, 0.0006580867733043499, 0.00438425882512285, 0.00438425882512285, 0.00438425882512285, 0.00438425882512285, 0.00438425882512285, 0.00438425882512285, 0.013830063842509817, 0.013830063842509817, 0.013830063842509817, 0.013830063842509817, 0.013830063842509817, 0.013830063842509817, 0.004240437424683717, 0.004240437424683717, 0.004240437424683717, 0.004240437424683717, 0.004240437424683717, 0.004240437424683717, 0.004240437424683717, 0.004240437424683717, 0.004240437424683717, 0.004240437424683717, 0.004240437424683717, 0.004240437424683717, 0.0022387397396142, 0.0022387397396142, 0.0022387397396142, 0.0022387397396142, 0.0022387397396142, 0.0022387397396142, 0.0022387397396142, 0.0022387397396142, 0.0022387397396142, 0.0022387397396142, 0.0022387397396142, 0.0022387397396142}; return {std::move(x), std::move(w)}; } else throw std::runtime_error("Keast not implemented for this order."); } else throw std::runtime_error("Keast not implemented for this cell type."); } //----------------------------------------------------------------------------- template std::array, 2> make_xiao_gimbutas_quadrature(cell::type celltype, int m) { if (celltype == cell::type::triangle) { if (m == 1) { // Xiao Gimbutas, 3 points, degree 1 std::vector x = {0.3333333333333333, 0.3333333333333333}; std::vector w = {0.5}; return {std::move(x), std::move(w)}; } else if (m == 2) { // Xiao Gimbutas, 3 points, degree 2 std::vector x = {0.16666666666666666, 0.16666666666666666, 0.16666666666666666, 0.6666666666666667, 0.6666666666666667, 0.16666666666666666}; std::vector w = {0.16666666666666666, 0.16666666666666666, 0.16666666666666666}; return {std::move(x), std::move(w)}; } else if (m == 3) { // Xiao Gimbutas, 3 points, degree 3 std::vector x = {0.4459484909159649, 0.4459484909159649, 0.09157621350977085, 0.09157621350977085, 0.4459484909159649, 0.10810301816807022, 0.09157621350977085, 0.8168475729804583, 0.10810301816807022, 0.4459484909159649, 0.8168475729804583, 0.09157621350977085}; std::vector w = {0.11169079483900574, 0.05497587182766094, 0.11169079483900574, 0.05497587182766094, 0.11169079483900574, 0.05497587182766094}; return {std::move(x), std::move(w)}; } else if (m == 4) { // Xiao Gimbutas, 3 points, degree 4 std::vector x = {0.4459484909159649, 0.4459484909159649, 0.09157621350977085, 0.09157621350977085, 0.4459484909159649, 0.10810301816807022, 0.09157621350977085, 0.8168475729804583, 0.10810301816807022, 0.4459484909159649, 0.8168475729804583, 0.09157621350977085}; std::vector w = {0.11169079483900574, 0.05497587182766094, 0.11169079483900574, 0.05497587182766094, 0.11169079483900574, 0.05497587182766094}; return {std::move(x), std::move(w)}; } else if (m == 5) { // Xiao Gimbutas, 3 points, degree 5 std::vector x = {0.3333333333333333, 0.3333333333333333, 0.1012865073234564, 0.1012865073234564, 0.47014206410511505, 0.47014206410511505, 0.1012865073234564, 0.7974269853530872, 0.47014206410511505, 0.05971587178976989, 0.7974269853530872, 0.1012865073234564, 0.05971587178976989, 0.47014206410511505}; std::vector w = {0.1125, 0.06296959027241357, 0.0661970763942531, 0.06296959027241357, 0.0661970763942531, 0.06296959027241357, 0.0661970763942531}; return {std::move(x), std::move(w)}; } else if (m == 6) { // Xiao Gimbutas, 3 points, degree 6 std::vector x = {0.21942998254978302, 0.21942998254978302, 0.48013796411221504, 0.48013796411221504, 0.21942998254978302, 0.561140034900434, 0.48013796411221504, 0.039724071775569914, 0.561140034900434, 0.21942998254978302, 0.039724071775569914, 0.48013796411221504, 0.019371724361240805, 0.14161901592396814, 0.8390092597147911, 0.019371724361240805, 0.14161901592396814, 0.8390092597147911, 0.14161901592396814, 0.019371724361240805, 0.8390092597147911, 0.14161901592396814, 0.019371724361240805, 0.8390092597147911}; std::vector w = {0.08566656207649052, 0.04036554479651549, 0.08566656207649052, 0.04036554479651549, 0.08566656207649052, 0.04036554479651549, 0.02031727989683033, 0.02031727989683033, 0.02031727989683033, 0.02031727989683033, 0.02031727989683033, 0.02031727989683033}; return {std::move(x), std::move(w)}; } else if (m == 7) { // Xiao Gimbutas, 3 points, degree 7 std::vector x = {0.47319565368925104, 0.47319565368925104, 0.057797640054506494, 0.057797640054506494, 0.24166360639724743, 0.24166360639724743, 0.47319565368925104, 0.05360869262149792, 0.057797640054506494, 0.884404719890987, 0.24166360639724743, 0.5166727872055051, 0.05360869262149792, 0.47319565368925104, 0.884404719890987, 0.057797640054506494, 0.5166727872055051, 0.24166360639724743, 0.046971206130085534, 0.2593390118657857, 0.6936897820041288, 0.046971206130085534, 0.2593390118657857, 0.6936897820041288, 0.2593390118657857, 0.046971206130085534, 0.6936897820041288, 0.2593390118657857, 0.046971206130085534, 0.6936897820041288}; std::vector w = {0.02659041664838023, 0.020459085197028434, 0.06386262428056692, 0.02659041664838023, 0.020459085197028434, 0.06386262428056692, 0.02659041664838023, 0.020459085197028434, 0.06386262428056692, 0.027877270270345547, 0.027877270270345547, 0.027877270270345547, 0.027877270270345547, 0.027877270270345547, 0.027877270270345547}; return {std::move(x), std::move(w)}; } else if (m == 8) { // Xiao Gimbutas, 3 points, degree 8 std::vector x = {0.3333333333333333, 0.3333333333333333, 0.17056930775176027, 0.17056930775176027, 0.4592925882927231, 0.4592925882927231, 0.05054722831703107, 0.05054722831703107, 0.17056930775176027, 0.6588613844964795, 0.4592925882927231, 0.08141482341455375, 0.05054722831703107, 0.8989055433659379, 0.6588613844964795, 0.17056930775176027, 0.08141482341455375, 0.4592925882927231, 0.8989055433659379, 0.05054722831703107, 0.008394777409957675, 0.26311282963463806, 0.7284923929554044, 0.008394777409957675, 0.26311282963463806, 0.7284923929554044, 0.26311282963463806, 0.008394777409957675, 0.7284923929554044, 0.26311282963463806, 0.008394777409957675, 0.7284923929554044}; std::vector w = {0.0721578038388936, 0.05160868526735912, 0.04754581713364232, 0.01622924881159904, 0.05160868526735912, 0.04754581713364232, 0.01622924881159904, 0.05160868526735912, 0.04754581713364232, 0.01622924881159904, 0.013615157087217498, 0.013615157087217498, 0.013615157087217498, 0.013615157087217498, 0.013615157087217498, 0.013615157087217498}; return {std::move(x), std::move(w)}; } else if (m == 9) { // Xiao Gimbutas, 3 points, degree 9 std::vector x = {0.3333333333333333, 0.3333333333333333, 0.4896825191987376, 0.4896825191987376, 0.1882035356190328, 0.1882035356190328, 0.43708959149293664, 0.43708959149293664, 0.04472951339445275, 0.04472951339445275, 0.4896825191987376, 0.02063496160252476, 0.1882035356190328, 0.6235929287619344, 0.43708959149293664, 0.12582081701412673, 0.04472951339445275, 0.9105409732110945, 0.02063496160252476, 0.4896825191987376, 0.6235929287619344, 0.1882035356190328, 0.12582081701412673, 0.43708959149293664, 0.9105409732110945, 0.04472951339445275, 0.0368384120547363, 0.2219629891607657, 0.741198598784498, 0.0368384120547363, 0.2219629891607657, 0.741198598784498, 0.2219629891607657, 0.0368384120547363, 0.741198598784498, 0.2219629891607657, 0.0368384120547363, 0.741198598784498}; std::vector w = {0.04856789814139942, 0.015667350113569536, 0.03982386946360513, 0.03891377050238714, 0.012788837829349017, 0.015667350113569536, 0.03982386946360513, 0.03891377050238714, 0.012788837829349017, 0.015667350113569536, 0.03982386946360513, 0.03891377050238714, 0.012788837829349017, 0.021641769688644688, 0.021641769688644688, 0.021641769688644688, 0.021641769688644688, 0.021641769688644688, 0.021641769688644688}; return {std::move(x), std::move(w)}; } else if (m == 10) { // Xiao Gimbutas, 3 points, degree 10 std::vector x = {0.3333333333333333, 0.3333333333333333, 0.4951734598011705, 0.4951734598011705, 0.019139415242841296, 0.019139415242841296, 0.18448501268524653, 0.18448501268524653, 0.42823482094371884, 0.42823482094371884, 0.4951734598011705, 0.009653080397658997, 0.019139415242841296, 0.9617211695143174, 0.18448501268524653, 0.6310299746295069, 0.42823482094371884, 0.14353035811256232, 0.009653080397658997, 0.4951734598011705, 0.9617211695143174, 0.019139415242841296, 0.6310299746295069, 0.18448501268524653, 0.14353035811256232, 0.42823482094371884, 0.03472362048232748, 0.13373475510086913, 0.03758272734119169, 0.3266931362813369, 0.8315416244168035, 0.03472362048232748, 0.6357241363774714, 0.03758272734119169, 0.13373475510086913, 0.8315416244168035, 0.3266931362813369, 0.6357241363774714, 0.13373475510086913, 0.03472362048232748, 0.3266931362813369, 0.03758272734119169, 0.8315416244168035, 0.13373475510086913, 0.6357241363774714, 0.3266931362813369, 0.03472362048232748, 0.8315416244168035, 0.03758272734119169, 0.6357241363774714}; std::vector w = {0.041807437186986963, 0.004896295249209152, 0.003192679615059327, 0.039316884873188636, 0.03762366398427199, 0.004896295249209152, 0.003192679615059327, 0.039316884873188636, 0.03762366398427199, 0.004896295249209152, 0.003192679615059327, 0.039316884873188636, 0.03762366398427199, 0.014481140731628171, 0.019369524543009452, 0.014481140731628171, 0.019369524543009452, 0.014481140731628171, 0.019369524543009452, 0.014481140731628171, 0.019369524543009452, 0.014481140731628171, 0.019369524543009452, 0.014481140731628171, 0.019369524543009452}; return {std::move(x), std::move(w)}; } else if (m == 11) { // Xiao Gimbutas, 3 points, degree 11 std::vector x = {0.3333333333333333, 0.3333333333333333, 0.030846895635588123, 0.030846895635588123, 0.49878016517846074, 0.49878016517846074, 0.11320782728669404, 0.11320782728669404, 0.4366550163931761, 0.4366550163931761, 0.21448345861926937, 0.21448345861926937, 0.030846895635588123, 0.9383062087288238, 0.49878016517846074, 0.0024396696430785125, 0.11320782728669404, 0.7735843454266119, 0.4366550163931761, 0.12668996721364778, 0.21448345861926937, 0.5710330827614613, 0.9383062087288238, 0.030846895635588123, 0.0024396696430785125, 0.49878016517846074, 0.7735843454266119, 0.11320782728669404, 0.12668996721364778, 0.4366550163931761, 0.5710330827614613, 0.21448345861926937, 0.014366662569555624, 0.1593036198376935, 0.04766406697215078, 0.31063121631346313, 0.8263297175927509, 0.014366662569555624, 0.6417047167143861, 0.04766406697215078, 0.1593036198376935, 0.8263297175927509, 0.31063121631346313, 0.6417047167143861, 0.1593036198376935, 0.014366662569555624, 0.31063121631346313, 0.04766406697215078, 0.8263297175927509, 0.1593036198376935, 0.6417047167143861, 0.31063121631346313, 0.014366662569555624, 0.8263297175927509, 0.04766406697215078, 0.6417047167143861}; std::vector w = { 0.040722567354675644, 0.006124648475353982, 0.0062327459369406904, 0.02006462119065416, 0.031547436079949344, 0.033922553871847574, 0.006124648475353982, 0.0062327459369406904, 0.02006462119065416, 0.031547436079949344, 0.033922553871847574, 0.006124648475353982, 0.0062327459369406904, 0.02006462119065416, 0.031547436079949344, 0.033922553871847574, 0.007278811668904623, 0.020321424327943236, 0.007278811668904623, 0.020321424327943236, 0.007278811668904623, 0.020321424327943236, 0.007278811668904623, 0.020321424327943236, 0.007278811668904623, 0.020321424327943236, 0.007278811668904623, 0.020321424327943236}; return {std::move(x), std::move(w)}; } else if (m == 12) { // Xiao Gimbutas, 3 points, degree 12 std::vector x = {0.27146250701492614, 0.27146250701492614, 0.10925782765935432, 0.10925782765935432, 0.4401116486585931, 0.4401116486585931, 0.4882037509455415, 0.4882037509455415, 0.02464636343633564, 0.02464636343633564, 0.27146250701492614, 0.45707498597014773, 0.10925782765935432, 0.7814843446812914, 0.4401116486585931, 0.11977670268281382, 0.4882037509455415, 0.02359249810891695, 0.02464636343633564, 0.9507072731273287, 0.45707498597014773, 0.27146250701492614, 0.7814843446812914, 0.10925782765935432, 0.11977670268281382, 0.4401116486585931, 0.02359249810891695, 0.4882037509455415, 0.9507072731273287, 0.02464636343633564, 0.1162960196779266, 0.25545422863851736, 0.021382490256170623, 0.12727971723358936, 0.023034156355267166, 0.29165567973834094, 0.6282497516835561, 0.1162960196779266, 0.85133779251024, 0.021382490256170623, 0.6853101639063919, 0.023034156355267166, 0.25545422863851736, 0.6282497516835561, 0.12727971723358936, 0.85133779251024, 0.29165567973834094, 0.6853101639063919, 0.25545422863851736, 0.1162960196779266, 0.12727971723358936, 0.021382490256170623, 0.29165567973834094, 0.023034156355267166, 0.6282497516835561, 0.25545422863851736, 0.85133779251024, 0.12727971723358936, 0.6853101639063919, 0.29165567973834094, 0.1162960196779266, 0.6282497516835561, 0.021382490256170623, 0.85133779251024, 0.023034156355267166, 0.6853101639063919}; std::vector w = { 0.03127060659795138, 0.014243026034438775, 0.024959167464030475, 0.012133419040726017, 0.0039658212549868194, 0.03127060659795138, 0.014243026034438775, 0.024959167464030475, 0.012133419040726017, 0.0039658212549868194, 0.03127060659795138, 0.014243026034438775, 0.024959167464030475, 0.012133419040726017, 0.0039658212549868194, 0.021613681829707104, 0.007541838788255721, 0.01089179251930378, 0.021613681829707104, 0.007541838788255721, 0.01089179251930378, 0.021613681829707104, 0.007541838788255721, 0.01089179251930378, 0.021613681829707104, 0.007541838788255721, 0.01089179251930378, 0.021613681829707104, 0.007541838788255721, 0.01089179251930378, 0.021613681829707104, 0.007541838788255721, 0.01089179251930378}; return {std::move(x), std::move(w)}; } else if (m == 13) { // Xiao Gimbutas, 3 points, degree 13 std::vector x = {0.3333333333333333, 0.3333333333333333, 0.4961358947410461, 0.4961358947410461, 0.4696086896534919, 0.4696086896534919, 0.23111028494908226, 0.23111028494908226, 0.4144775702790546, 0.4144775702790546, 0.11355991257213327, 0.11355991257213327, 0.024895931491216494, 0.024895931491216494, 0.4961358947410461, 0.007728210517907841, 0.4696086896534919, 0.06078262069301621, 0.23111028494908226, 0.5377794301018355, 0.4144775702790546, 0.17104485944189085, 0.11355991257213327, 0.7728801748557335, 0.024895931491216494, 0.950208137017567, 0.007728210517907841, 0.4961358947410461, 0.06078262069301621, 0.4696086896534919, 0.5377794301018355, 0.23111028494908226, 0.17104485944189085, 0.4144775702790546, 0.7728801748557335, 0.11355991257213327, 0.950208137017567, 0.024895931491216494, 0.01898800438375904, 0.2920786885766364, 0.09773603106601653, 0.26674525331035115, 0.021966344206529244, 0.1267997757838373, 0.6889333070396046, 0.01898800438375904, 0.6355187156236324, 0.09773603106601653, 0.8512338800096335, 0.021966344206529244, 0.2920786885766364, 0.6889333070396046, 0.26674525331035115, 0.6355187156236324, 0.1267997757838373, 0.8512338800096335, 0.2920786885766364, 0.01898800438375904, 0.26674525331035115, 0.09773603106601653, 0.1267997757838373, 0.021966344206529244, 0.6889333070396046, 0.2920786885766364, 0.6355187156236324, 0.26674525331035115, 0.8512338800096335, 0.1267997757838373, 0.01898800438375904, 0.6889333070396046, 0.09773603106601653, 0.6355187156236324, 0.021966344206529244, 0.8512338800096335}; std::vector w = {0.02581132333214541, 0.004970738180536294, 0.01639062080186149, 0.023031204796389124, 0.0234735477710776, 0.015451548987879897, 0.0040146998976292115, 0.004970738180536294, 0.01639062080186149, 0.023031204796389124, 0.0234735477710776, 0.015451548987879897, 0.0040146998976292115, 0.004970738180536294, 0.01639062080186149, 0.023031204796389124, 0.0234735477710776, 0.015451548987879897, 0.0040146998976292115, 0.00906274932310044, 0.018605980228630768, 0.007696536341891089, 0.00906274932310044, 0.018605980228630768, 0.007696536341891089, 0.00906274932310044, 0.018605980228630768, 0.007696536341891089, 0.00906274932310044, 0.018605980228630768, 0.007696536341891089, 0.00906274932310044, 0.018605980228630768, 0.007696536341891089, 0.00906274932310044, 0.018605980228630768, 0.007696536341891089}; return {std::move(x), std::move(w)}; } else if (m == 14) { // Xiao Gimbutas, 3 points, degree 14 std::vector x = {0.41764471934045394, 0.41764471934045394, 0.0617998830908727, 0.0617998830908727, 0.2734775283088387, 0.2734775283088387, 0.1772055324125435, 0.1772055324125435, 0.0193909612487011, 0.0193909612487011, 0.4889639103621786, 0.4889639103621786, 0.41764471934045394, 0.16471056131909212, 0.0617998830908727, 0.8764002338182546, 0.2734775283088387, 0.4530449433823226, 0.1772055324125435, 0.645588935174913, 0.0193909612487011, 0.9612180775025978, 0.4889639103621786, 0.022072179275642756, 0.16471056131909212, 0.41764471934045394, 0.8764002338182546, 0.0617998830908727, 0.4530449433823226, 0.2734775283088387, 0.645588935174913, 0.1772055324125435, 0.9612180775025978, 0.0193909612487011, 0.022072179275642756, 0.4889639103621786, 0.014646950055654471, 0.29837288213625773, 0.09291624935697185, 0.336861459796345, 0.05712475740364799, 0.17226668782135557, 0.001268330932872076, 0.11897449769695682, 0.6869801678080878, 0.014646950055654471, 0.5702222908466832, 0.09291624935697185, 0.7706085547749965, 0.05712475740364799, 0.8797571713701712, 0.001268330932872076, 0.29837288213625773, 0.6869801678080878, 0.336861459796345, 0.5702222908466832, 0.17226668782135557, 0.7706085547749965, 0.11897449769695682, 0.8797571713701712, 0.29837288213625773, 0.014646950055654471, 0.336861459796345, 0.09291624935697185, 0.17226668782135557, 0.05712475740364799, 0.11897449769695682, 0.001268330932872076, 0.6869801678080878, 0.29837288213625773, 0.5702222908466832, 0.336861459796345, 0.7706085547749965, 0.17226668782135557, 0.8797571713701712, 0.11897449769695682, 0.014646950055654471, 0.6869801678080878, 0.09291624935697185, 0.5702222908466832, 0.05712475740364799, 0.7706085547749965, 0.001268330932872076, 0.8797571713701712}; std::vector w = {0.016394176772062678, 0.007216849834888334, 0.025887052253645793, 0.02108129436849651, 0.002461701801200041, 0.010941790684714446, 0.016394176772062678, 0.007216849834888334, 0.025887052253645793, 0.02108129436849651, 0.002461701801200041, 0.010941790684714446, 0.016394176772062678, 0.007216849834888334, 0.025887052253645793, 0.02108129436849651, 0.002461701801200041, 0.010941790684714446, 0.007218154056766921, 0.019285755393530342, 0.012332876606281839, 0.002505114419250336, 0.007218154056766921, 0.019285755393530342, 0.012332876606281839, 0.002505114419250336, 0.007218154056766921, 0.019285755393530342, 0.012332876606281839, 0.002505114419250336, 0.007218154056766921, 0.019285755393530342, 0.012332876606281839, 0.002505114419250336, 0.007218154056766921, 0.019285755393530342, 0.012332876606281839, 0.002505114419250336, 0.007218154056766921, 0.019285755393530342, 0.012332876606281839, 0.002505114419250336}; return {std::move(x), std::move(w)}; } else if (m == 15) { // Xiao Gimbutas, 3 points, degree 15 std::vector x = { 0.3333333333333333, 0.3333333333333333, 0.1299782299330779, 0.1299782299330779, 0.4600769492970597, 0.4600769492970597, 0.4916858166302972, 0.4916858166302972, 0.22153234079514206, 0.22153234079514206, 0.39693373740906057, 0.39693373740906057, 0.0563419176961002, 0.0563419176961002, 0.1299782299330779, 0.7400435401338442, 0.4600769492970597, 0.07984610140588055, 0.4916858166302972, 0.016628366739405598, 0.22153234079514206, 0.5569353184097159, 0.39693373740906057, 0.20613252518187886, 0.0563419176961002, 0.8873161646077996, 0.7400435401338442, 0.1299782299330779, 0.07984610140588055, 0.4600769492970597, 0.016628366739405598, 0.4916858166302972, 0.5569353184097159, 0.22153234079514206, 0.20613252518187886, 0.39693373740906057, 0.8873161646077996, 0.0563419176961002, 0.08459422148219181, 0.18232178340719132, 0.016027089786345473, 0.15020038406523872, 0.09765044243024235, 0.32311131516371266, 0.018454251904633165, 0.3079476814836729, 0.0011135352740137417, 0.03803522930110929, 0.733083995110617, 0.08459422148219181, 0.8337725261484158, 0.016027089786345473, 0.5792382424060449, 0.09765044243024235, 0.673598066611694, 0.018454251904633165, 0.960851235424877, 0.0011135352740137417, 0.18232178340719132, 0.733083995110617, 0.15020038406523872, 0.8337725261484158, 0.32311131516371266, 0.5792382424060449, 0.3079476814836729, 0.673598066611694, 0.03803522930110929, 0.960851235424877, 0.18232178340719132, 0.08459422148219181, 0.15020038406523872, 0.016027089786345473, 0.32311131516371266, 0.09765044243024235, 0.3079476814836729, 0.018454251904633165, 0.03803522930110929, 0.0011135352740137417, 0.733083995110617, 0.18232178340719132, 0.8337725261484158, 0.15020038406523872, 0.5792382424060449, 0.32311131516371266, 0.673598066611694, 0.3079476814836729, 0.960851235424877, 0.03803522930110929, 0.08459422148219181, 0.733083995110617, 0.016027089786345473, 0.8337725261484158, 0.09765044243024235, 0.5792382424060449, 0.018454251904633165, 0.673598066611694, 0.0011135352740137417, 0.960851235424877}; std::vector w = { 0.01486520987403566, 0.00369875203352305, 0.010797043968219226, 0.0079161381750109, 0.023143643052599038, 0.023168020695603617, 0.007542237123798534, 0.00369875203352305, 0.010797043968219226, 0.0079161381750109, 0.023143643052599038, 0.023168020695603617, 0.007542237123798534, 0.00369875203352305, 0.010797043968219226, 0.0079161381750109, 0.023143643052599038, 0.023168020695603617, 0.007542237123798534, 0.012115004391562803, 0.00561425214943903, 0.015537610235255475, 0.008218381046413948, 0.0012376330072789582, 0.012115004391562803, 0.00561425214943903, 0.015537610235255475, 0.008218381046413948, 0.0012376330072789582, 0.012115004391562803, 0.00561425214943903, 0.015537610235255475, 0.008218381046413948, 0.0012376330072789582, 0.012115004391562803, 0.00561425214943903, 0.015537610235255475, 0.008218381046413948, 0.0012376330072789582, 0.012115004391562803, 0.00561425214943903, 0.015537610235255475, 0.008218381046413948, 0.0012376330072789582, 0.012115004391562803, 0.00561425214943903, 0.015537610235255475, 0.008218381046413948, 0.0012376330072789582}; return {std::move(x), std::move(w)}; } else if (m == 16) { // Xiao Gimbutas, 3 points, degree 16 std::vector x = {0.3333333333333333, 0.3333333333333333, 0.06667447224023837, 0.06667447224023837, 0.24132168070137838, 0.24132168070137838, 0.41279809595522365, 0.41279809595522365, 0.15006373658703515, 0.15006373658703515, 0.46954803099668496, 0.46954803099668496, 0.017041629405718517, 0.017041629405718517, 0.06667447224023837, 0.8666510555195233, 0.24132168070137838, 0.5173566385972432, 0.41279809595522365, 0.1744038080895527, 0.15006373658703515, 0.6998725268259297, 0.46954803099668496, 0.060903938006630076, 0.017041629405718517, 0.965916741188563, 0.8666510555195233, 0.06667447224023837, 0.5173566385972432, 0.24132168070137838, 0.1744038080895527, 0.41279809595522365, 0.6998725268259297, 0.15006373658703515, 0.060903938006630076, 0.46954803099668496, 0.965916741188563, 0.017041629405718517, 0.009664954403660254, 0.41376948582708517, 0.030305943355186365, 0.30417944822947973, 0.010812972776103751, 0.08960908902270585, 0.10665316053614844, 0.29661537240038294, 0.051354315344013114, 0.16976335515028973, 0.0036969427073556124, 0.21404877992584728, 0.5765655597692546, 0.009664954403660254, 0.6655146084153339, 0.030305943355186365, 0.8995779382011905, 0.010812972776103751, 0.5967314670634686, 0.10665316053614844, 0.7788823295056971, 0.051354315344013114, 0.7822542773667971, 0.0036969427073556124, 0.41376948582708517, 0.5765655597692546, 0.30417944822947973, 0.6655146084153339, 0.08960908902270585, 0.8995779382011905, 0.29661537240038294, 0.5967314670634686, 0.16976335515028973, 0.7788823295056971, 0.21404877992584728, 0.7822542773667971, 0.41376948582708517, 0.009664954403660254, 0.30417944822947973, 0.030305943355186365, 0.08960908902270585, 0.010812972776103751, 0.29661537240038294, 0.10665316053614844, 0.16976335515028973, 0.051354315344013114, 0.21404877992584728, 0.0036969427073556124, 0.5765655597692546, 0.41376948582708517, 0.6655146084153339, 0.30417944822947973, 0.8995779382011905, 0.08960908902270585, 0.5967314670634686, 0.29661537240038294, 0.7788823295056971, 0.16976335515028973, 0.7822542773667971, 0.21404877992584728, 0.009664954403660254, 0.5765655597692546, 0.030305943355186365, 0.6655146084153339, 0.010812972776103751, 0.8995779382011905, 0.10665316053614844, 0.5967314670634686, 0.051354315344013114, 0.7788823295056971, 0.0036969427073556124, 0.7822542773667971}; std::vector w = {0.023113955157095672, 0.006212712797780504, 0.020592020534896276, 0.020492609893407683, 0.014391748351374455, 0.013546834733855226, 0.001894567619132111, 0.006212712797780504, 0.020592020534896276, 0.020492609893407683, 0.014391748351374455, 0.013546834733855226, 0.001894567619132111, 0.006212712797780504, 0.020592020534896276, 0.020492609893407683, 0.014391748351374455, 0.013546834733855226, 0.001894567619132111, 0.004091105276611069, 0.006991803562326784, 0.0028759349852485795, 0.015823030840991622, 0.008826540523551642, 0.0023073453198645673, 0.004091105276611069, 0.006991803562326784, 0.0028759349852485795, 0.015823030840991622, 0.008826540523551642, 0.0023073453198645673, 0.004091105276611069, 0.006991803562326784, 0.0028759349852485795, 0.015823030840991622, 0.008826540523551642, 0.0023073453198645673, 0.004091105276611069, 0.006991803562326784, 0.0028759349852485795, 0.015823030840991622, 0.008826540523551642, 0.0023073453198645673, 0.004091105276611069, 0.006991803562326784, 0.0028759349852485795, 0.015823030840991622, 0.008826540523551642, 0.0023073453198645673, 0.004091105276611069, 0.006991803562326784, 0.0028759349852485795, 0.015823030840991622, 0.008826540523551642, 0.0023073453198645673}; return {std::move(x), std::move(w)}; } else if (m == 17) { // Xiao Gimbutas, 3 points, degree 17 std::vector x = {0.4171034443615992, 0.4171034443615992, 0.18035811626637066, 0.18035811626637066, 0.2857065024365867, 0.2857065024365867, 0.06665406347959701, 0.06665406347959701, 0.014755491660754072, 0.014755491660754072, 0.46559787161889027, 0.46559787161889027, 0.4171034443615992, 0.16579311127680163, 0.18035811626637066, 0.6392837674672587, 0.2857065024365867, 0.42858699512682663, 0.06665406347959701, 0.866691873040806, 0.014755491660754072, 0.9704890166784919, 0.46559787161889027, 0.06880425676221946, 0.16579311127680163, 0.4171034443615992, 0.6392837674672587, 0.18035811626637066, 0.42858699512682663, 0.2857065024365867, 0.866691873040806, 0.06665406347959701, 0.9704890166784919, 0.014755491660754072, 0.06880425676221946, 0.46559787161889027, 0.011575175903180683, 0.07250547079900238, 0.013229672760086951, 0.41547545929522905, 0.013135870834002753, 0.27179187005535477, 0.15750547792686992, 0.29921894247697034, 0.06734937786736123, 0.3062815917461865, 0.07804234056828245, 0.16872251349525944, 0.016017642362119337, 0.15919228747279268, 0.9159193532978169, 0.011575175903180683, 0.5712948679446841, 0.013229672760086951, 0.7150722591106424, 0.013135870834002753, 0.5432755795961598, 0.15750547792686992, 0.6263690303864522, 0.06734937786736123, 0.7532351459364581, 0.07804234056828245, 0.824790070165088, 0.016017642362119337, 0.07250547079900238, 0.9159193532978169, 0.41547545929522905, 0.5712948679446841, 0.27179187005535477, 0.7150722591106424, 0.29921894247697034, 0.5432755795961598, 0.3062815917461865, 0.6263690303864522, 0.16872251349525944, 0.7532351459364581, 0.15919228747279268, 0.824790070165088, 0.07250547079900238, 0.011575175903180683, 0.41547545929522905, 0.013229672760086951, 0.27179187005535477, 0.013135870834002753, 0.29921894247697034, 0.15750547792686992, 0.3062815917461865, 0.06734937786736123, 0.16872251349525944, 0.07804234056828245, 0.15919228747279268, 0.016017642362119337, 0.9159193532978169, 0.07250547079900238, 0.5712948679446841, 0.41547545929522905, 0.7150722591106424, 0.27179187005535477, 0.5432755795961598, 0.29921894247697034, 0.6263690303864522, 0.3062815917461865, 0.7532351459364581, 0.16872251349525944, 0.824790070165088, 0.15919228747279268, 0.011575175903180683, 0.9159193532978169, 0.013229672760086951, 0.5712948679446841, 0.013135870834002753, 0.7150722591106424, 0.15750547792686992, 0.5432755795961598, 0.06734937786736123, 0.6263690303864522, 0.07804234056828245, 0.7532351459364581, 0.016017642362119337, 0.824790070165088}; std::vector w = {0.013655463264051053, 0.013156315294008993, 0.01885811857639764, 0.006229500401152722, 0.001386943788818821, 0.01250972547524868, 0.013655463264051053, 0.013156315294008993, 0.01885811857639764, 0.006229500401152722, 0.001386943788818821, 0.01250972547524868, 0.013655463264051053, 0.013156315294008993, 0.01885811857639764, 0.006229500401152722, 0.001386943788818821, 0.01250972547524868, 0.002292174200867934, 0.005199219977919768, 0.004346107250500596, 0.013085812967668494, 0.011243886273345534, 0.01027894916022726, 0.003989150102964797, 0.002292174200867934, 0.005199219977919768, 0.004346107250500596, 0.013085812967668494, 0.011243886273345534, 0.01027894916022726, 0.003989150102964797, 0.002292174200867934, 0.005199219977919768, 0.004346107250500596, 0.013085812967668494, 0.011243886273345534, 0.01027894916022726, 0.003989150102964797, 0.002292174200867934, 0.005199219977919768, 0.004346107250500596, 0.013085812967668494, 0.011243886273345534, 0.01027894916022726, 0.003989150102964797, 0.002292174200867934, 0.005199219977919768, 0.004346107250500596, 0.013085812967668494, 0.011243886273345534, 0.01027894916022726, 0.003989150102964797, 0.002292174200867934, 0.005199219977919768, 0.004346107250500596, 0.013085812967668494, 0.011243886273345534, 0.01027894916022726, 0.003989150102964797}; return {std::move(x), std::move(w)}; } else if (m == 18) { // Xiao Gimbutas, 3 points, degree 18 std::vector x = {0.3333333333333333, 0.3333333333333333, 0.4749182113240457, 0.4749182113240457, 0.15163850697260495, 0.15163850697260495, 0.4110671018759195, 0.4110671018759195, 0.2656146099053742, 0.2656146099053742, 0.0037589443410684376, 0.0037589443410684376, 0.072438705567333, 0.072438705567333, 0.4749182113240457, 0.05016357735190857, 0.15163850697260495, 0.6967229860547901, 0.4110671018759195, 0.177865796248161, 0.2656146099053742, 0.46877078018925156, 0.0037589443410684376, 0.9924821113178631, 0.072438705567333, 0.855122588865334, 0.05016357735190857, 0.4749182113240457, 0.6967229860547901, 0.15163850697260495, 0.177865796248161, 0.4110671018759195, 0.46877078018925156, 0.2656146099053742, 0.9924821113178631, 0.0037589443410684376, 0.855122588865334, 0.072438705567333, 0.09042704035434063, 0.3850440344131637, 0.012498932483495477, 0.04727614183265175, 0.05401173533902428, 0.30206195771287075, 0.010505018819241962, 0.2565061597742415, 0.06612245802840343, 0.17847912556588763, 0.14906691012577386, 0.2685733063960138, 0.011691824674667157, 0.41106566867461836, 0.014331524778941987, 0.1327788302713893, 0.5245289252324957, 0.09042704035434063, 0.9402249256838529, 0.012498932483495477, 0.6439263069481049, 0.05401173533902428, 0.7329888214065166, 0.010505018819241962, 0.7553984164057089, 0.06612245802840343, 0.5823597834782124, 0.14906691012577386, 0.5772425066507145, 0.011691824674667157, 0.8528896449496688, 0.014331524778941987, 0.3850440344131637, 0.5245289252324957, 0.04727614183265175, 0.9402249256838529, 0.30206195771287075, 0.6439263069481049, 0.2565061597742415, 0.7329888214065166, 0.17847912556588763, 0.7553984164057089, 0.2685733063960138, 0.5823597834782124, 0.41106566867461836, 0.5772425066507145, 0.1327788302713893, 0.8528896449496688, 0.3850440344131637, 0.09042704035434063, 0.04727614183265175, 0.012498932483495477, 0.30206195771287075, 0.05401173533902428, 0.2565061597742415, 0.010505018819241962, 0.17847912556588763, 0.06612245802840343, 0.2685733063960138, 0.14906691012577386, 0.41106566867461836, 0.011691824674667157, 0.1327788302713893, 0.014331524778941987, 0.5245289252324957, 0.3850440344131637, 0.9402249256838529, 0.04727614183265175, 0.6439263069481049, 0.30206195771287075, 0.7329888214065166, 0.2565061597742415, 0.7553984164057089, 0.17847912556588763, 0.5823597834782124, 0.2685733063960138, 0.5772425066507145, 0.41106566867461836, 0.8528896449496688, 0.1327788302713893, 0.09042704035434063, 0.5245289252324957, 0.012498932483495477, 0.9402249256838529, 0.05401173533902428, 0.6439263069481049, 0.010505018819241962, 0.7329888214065166, 0.06612245802840343, 0.7553984164057089, 0.14906691012577386, 0.5823597834782124, 0.011691824674667157, 0.5772425066507145, 0.014331524778941987, 0.8528896449496688}; std::vector w = { 0.01537426061955793, 0.006553513745869378, 0.0101591694227292, 0.01673599702992395, 0.015558198301003067, 0.0002660028084738903, 0.006895143302383471, 0.006553513745869378, 0.0101591694227292, 0.01673599702992395, 0.015558198301003067, 0.0002660028084738903, 0.006895143302383471, 0.006553513745869378, 0.0101591694227292, 0.01673599702992395, 0.015558198301003067, 0.0002660028084738903, 0.006895143302383471, 0.007664129097276571, 0.0021087583873722216, 0.008182954206993283, 0.0038649176400031137, 0.00845582695874004, 0.01379644324428974, 0.004793062237180752, 0.0038208524863598183, 0.007664129097276571, 0.0021087583873722216, 0.008182954206993283, 0.0038649176400031137, 0.00845582695874004, 0.01379644324428974, 0.004793062237180752, 0.0038208524863598183, 0.007664129097276571, 0.0021087583873722216, 0.008182954206993283, 0.0038649176400031137, 0.00845582695874004, 0.01379644324428974, 0.004793062237180752, 0.0038208524863598183, 0.007664129097276571, 0.0021087583873722216, 0.008182954206993283, 0.0038649176400031137, 0.00845582695874004, 0.01379644324428974, 0.004793062237180752, 0.0038208524863598183, 0.007664129097276571, 0.0021087583873722216, 0.008182954206993283, 0.0038649176400031137, 0.00845582695874004, 0.01379644324428974, 0.004793062237180752, 0.0038208524863598183, 0.007664129097276571, 0.0021087583873722216, 0.008182954206993283, 0.0038649176400031137, 0.00845582695874004, 0.01379644324428974, 0.004793062237180752, 0.0038208524863598183}; return {std::move(x), std::move(w)}; } else if (m == 19) { // Xiao Gimbutas, 3 points, degree 19 std::vector x = { 0.3333333333333333, 0.3333333333333333, 0.05252627985410363, 0.05252627985410363, 0.11144805571699878, 0.11144805571699878, 0.011639027327922657, 0.011639027327922657, 0.25516213315312486, 0.25516213315312486, 0.4039697179663861, 0.4039697179663861, 0.17817100607962755, 0.17817100607962755, 0.4591943889568276, 0.4591943889568276, 0.4925124498658742, 0.4925124498658742, 0.05252627985410363, 0.8949474402917927, 0.11144805571699878, 0.7771038885660024, 0.011639027327922657, 0.9767219453441547, 0.25516213315312486, 0.4896757336937503, 0.4039697179663861, 0.19206056406722782, 0.17817100607962755, 0.6436579878407449, 0.4591943889568276, 0.08161122208634475, 0.4925124498658742, 0.014975100268251551, 0.8949474402917927, 0.05252627985410363, 0.7771038885660024, 0.11144805571699878, 0.9767219453441547, 0.011639027327922657, 0.4896757336937503, 0.25516213315312486, 0.19206056406722782, 0.4039697179663861, 0.6436579878407449, 0.17817100607962755, 0.08161122208634475, 0.4591943889568276, 0.014975100268251551, 0.4925124498658742, 0.005005142352350433, 0.1424222825711269, 0.009777061438676854, 0.06008389996270236, 0.039142449434608845, 0.13070066996053453, 0.129312809767979, 0.31131838322398686, 0.07456118930435514, 0.22143394188911344, 0.04088831446497813, 0.3540259269997119, 0.014923638907438481, 0.24189410400689262, 0.0020691038491023883, 0.36462041433871, 0.8525725750765227, 0.005005142352350433, 0.9301390385986208, 0.009777061438676854, 0.8301568806048566, 0.039142449434608845, 0.5593688070080342, 0.129312809767979, 0.7040048688065313, 0.07456118930435514, 0.60508575853531, 0.04088831446497813, 0.7431822570856689, 0.014923638907438481, 0.6333104818121875, 0.0020691038491023883, 0.1424222825711269, 0.8525725750765227, 0.06008389996270236, 0.9301390385986208, 0.13070066996053453, 0.8301568806048566, 0.31131838322398686, 0.5593688070080342, 0.22143394188911344, 0.7040048688065313, 0.3540259269997119, 0.60508575853531, 0.24189410400689262, 0.7431822570856689, 0.36462041433871, 0.6333104818121875, 0.1424222825711269, 0.005005142352350433, 0.06008389996270236, 0.009777061438676854, 0.13070066996053453, 0.039142449434608845, 0.31131838322398686, 0.129312809767979, 0.22143394188911344, 0.07456118930435514, 0.3540259269997119, 0.04088831446497813, 0.24189410400689262, 0.014923638907438481, 0.36462041433871, 0.0020691038491023883, 0.8525725750765227, 0.1424222825711269, 0.9301390385986208, 0.06008389996270236, 0.8301568806048566, 0.13070066996053453, 0.5593688070080342, 0.31131838322398686, 0.7040048688065313, 0.22143394188911344, 0.60508575853531, 0.3540259269997119, 0.7431822570856689, 0.24189410400689262, 0.6333104818121875, 0.36462041433871, 0.005005142352350433, 0.8525725750765227, 0.009777061438676854, 0.9301390385986208, 0.039142449434608845, 0.8301568806048566, 0.129312809767979, 0.5593688070080342, 0.07456118930435514, 0.7040048688065313, 0.04088831446497813, 0.60508575853531, 0.014923638907438481, 0.7431822570856689, 0.0020691038491023883, 0.6333104818121875}; std::vector w = { 0.017234580425452638, 0.0035546968113974735, 0.007617478258502418, 0.0008825962091542701, 0.01587642729376499, 0.01576867932261981, 0.012325990526792415, 0.011491785488561626, 0.005160941091209432, 0.0035546968113974735, 0.007617478258502418, 0.0008825962091542701, 0.01587642729376499, 0.01576867932261981, 0.012325990526792415, 0.011491785488561626, 0.005160941091209432, 0.0035546968113974735, 0.007617478258502418, 0.0008825962091542701, 0.01587642729376499, 0.01576867932261981, 0.012325990526792415, 0.011491785488561626, 0.005160941091209432, 0.0014628462439400358, 0.0016636944202969523, 0.004847759540812101, 0.013173132353722682, 0.009054037295215252, 0.008051104730469714, 0.00422796241954674, 0.0016410687574198689, 0.0014628462439400358, 0.0016636944202969523, 0.004847759540812101, 0.013173132353722682, 0.009054037295215252, 0.008051104730469714, 0.00422796241954674, 0.0016410687574198689, 0.0014628462439400358, 0.0016636944202969523, 0.004847759540812101, 0.013173132353722682, 0.009054037295215252, 0.008051104730469714, 0.00422796241954674, 0.0016410687574198689, 0.0014628462439400358, 0.0016636944202969523, 0.004847759540812101, 0.013173132353722682, 0.009054037295215252, 0.008051104730469714, 0.00422796241954674, 0.0016410687574198689, 0.0014628462439400358, 0.0016636944202969523, 0.004847759540812101, 0.013173132353722682, 0.009054037295215252, 0.008051104730469714, 0.00422796241954674, 0.0016410687574198689, 0.0014628462439400358, 0.0016636944202969523, 0.004847759540812101, 0.013173132353722682, 0.009054037295215252, 0.008051104730469714, 0.00422796241954674, 0.0016410687574198689}; return {std::move(x), std::move(w)}; } else if (m == 20) { // Xiao Gimbutas, 3 points, degree 20 std::vector x = {0.3333333333333333, 0.3333333333333333, 0.18629499774454095, 0.18629499774454095, 0.037310880598884766, 0.037310880598884766, 0.476245611540499, 0.476245611540499, 0.4455510569559248, 0.4455510569559248, 0.25457926767333916, 0.25457926767333916, 0.39342534781709987, 0.39342534781709987, 0.01097614102839789, 0.01097614102839789, 0.10938359671171471, 0.10938359671171471, 0.18629499774454095, 0.6274100045109181, 0.037310880598884766, 0.9253782388022305, 0.476245611540499, 0.047508776919002016, 0.4455510569559248, 0.10889788608815043, 0.25457926767333916, 0.4908414646533217, 0.39342534781709987, 0.21314930436580026, 0.01097614102839789, 0.9780477179432042, 0.10938359671171471, 0.7812328065765706, 0.6274100045109181, 0.18629499774454095, 0.9253782388022305, 0.037310880598884766, 0.047508776919002016, 0.476245611540499, 0.10889788608815043, 0.4455510569559248, 0.4908414646533217, 0.25457926767333916, 0.21314930436580026, 0.39342534781709987, 0.9780477179432042, 0.01097614102839789, 0.7812328065765706, 0.10938359671171471, 0.004854937607623827, 0.06409058560843404, 0.10622720472027006, 0.2156070573900944, 0.007570780504696579, 0.15913370765706722, 0.13980807199179993, 0.317860123835772, 0.04656036490766434, 0.19851813222878817, 0.038363684775374655, 0.09995229628813862, 0.009831548292802588, 0.42002375881622406, 0.05498747914298685, 0.33313481730958744, 0.01073721285601111, 0.2805814114236652, 0.9310544767839422, 0.004854937607623827, 0.6781657378896355, 0.10622720472027006, 0.8332955118382361, 0.007570780504696579, 0.5423318041724281, 0.13980807199179993, 0.7549215028635474, 0.04656036490766434, 0.8616840189364867, 0.038363684775374655, 0.5701446928909732, 0.009831548292802588, 0.6118777035474257, 0.05498747914298685, 0.7086813757203236, 0.01073721285601111, 0.06409058560843404, 0.9310544767839422, 0.2156070573900944, 0.6781657378896355, 0.15913370765706722, 0.8332955118382361, 0.317860123835772, 0.5423318041724281, 0.19851813222878817, 0.7549215028635474, 0.09995229628813862, 0.8616840189364867, 0.42002375881622406, 0.5701446928909732, 0.33313481730958744, 0.6118777035474257, 0.2805814114236652, 0.7086813757203236, 0.06409058560843404, 0.004854937607623827, 0.2156070573900944, 0.10622720472027006, 0.15913370765706722, 0.007570780504696579, 0.317860123835772, 0.13980807199179993, 0.19851813222878817, 0.04656036490766434, 0.09995229628813862, 0.038363684775374655, 0.42002375881622406, 0.009831548292802588, 0.33313481730958744, 0.05498747914298685, 0.2805814114236652, 0.01073721285601111, 0.9310544767839422, 0.06409058560843404, 0.6781657378896355, 0.2156070573900944, 0.8332955118382361, 0.15913370765706722, 0.5423318041724281, 0.317860123835772, 0.7549215028635474, 0.19851813222878817, 0.8616840189364867, 0.09995229628813862, 0.5701446928909732, 0.42002375881622406, 0.6118777035474257, 0.33313481730958744, 0.7086813757203236, 0.2805814114236652, 0.004854937607623827, 0.9310544767839422, 0.10622720472027006, 0.6781657378896355, 0.007570780504696579, 0.8332955118382361, 0.13980807199179993, 0.5423318041724281, 0.04656036490766434, 0.7549215028635474, 0.038363684775374655, 0.8616840189364867, 0.009831548292802588, 0.5701446928909732, 0.05498747914298685, 0.6118777035474257, 0.01073721285601111, 0.7086813757203236}; std::vector w = { 0.013910110701453116, 0.009173462974252915, 0.0021612754106655778, 0.007101825303408441, 0.009452399933232448, 0.014083201307520249, 0.013788050629070459, 0.00079884079106662, 0.007830230776074535, 0.009173462974252915, 0.0021612754106655778, 0.007101825303408441, 0.009452399933232448, 0.014083201307520249, 0.013788050629070459, 0.00079884079106662, 0.007830230776074535, 0.009173462974252915, 0.0021612754106655778, 0.007101825303408441, 0.009452399933232448, 0.014083201307520249, 0.013788050629070459, 0.00079884079106662, 0.007830230776074535, 0.0011298696021258656, 0.007722607822099231, 0.002202897418558498, 0.011691745731827735, 0.00598639857895469, 0.004145711527613858, 0.003695681500255298, 0.008667225567219335, 0.0035782002384576856, 0.0011298696021258656, 0.007722607822099231, 0.002202897418558498, 0.011691745731827735, 0.00598639857895469, 0.004145711527613858, 0.003695681500255298, 0.008667225567219335, 0.0035782002384576856, 0.0011298696021258656, 0.007722607822099231, 0.002202897418558498, 0.011691745731827735, 0.00598639857895469, 0.004145711527613858, 0.003695681500255298, 0.008667225567219335, 0.0035782002384576856, 0.0011298696021258656, 0.007722607822099231, 0.002202897418558498, 0.011691745731827735, 0.00598639857895469, 0.004145711527613858, 0.003695681500255298, 0.008667225567219335, 0.0035782002384576856, 0.0011298696021258656, 0.007722607822099231, 0.002202897418558498, 0.011691745731827735, 0.00598639857895469, 0.004145711527613858, 0.003695681500255298, 0.008667225567219335, 0.0035782002384576856, 0.0011298696021258656, 0.007722607822099231, 0.002202897418558498, 0.011691745731827735, 0.00598639857895469, 0.004145711527613858, 0.003695681500255298, 0.008667225567219335, 0.0035782002384576856}; return {std::move(x), std::move(w)}; } else if (m == 21) { // Xiao Gimbutas, 3 points, degree 21 std::vector x = {0.2989362353149826, 0.2989362353149826, 0.4970078754686856, 0.4970078754686856, 0.40361758654638513, 0.40361758654638513, 0.11898857762271953, 0.11898857762271953, 0.19028871809127856, 0.19028871809127856, 0.4815978686532166, 0.4815978686532166, 0.4498127917753624, 0.4498127917753624, 0.053627575546145, 0.053627575546145, 0.010742456432828507, 0.010742456432828507, 0.2989362353149826, 0.4021275293700348, 0.4970078754686856, 0.005984249062628844, 0.40361758654638513, 0.19276482690722974, 0.11898857762271953, 0.762022844754561, 0.19028871809127856, 0.6194225638174429, 0.4815978686532166, 0.03680426269356685, 0.4498127917753624, 0.10037441644927525, 0.053627575546145, 0.89274484890771, 0.010742456432828507, 0.978515087134343, 0.4021275293700348, 0.2989362353149826, 0.005984249062628844, 0.4970078754686856, 0.19276482690722974, 0.40361758654638513, 0.762022844754561, 0.11898857762271953, 0.6194225638174429, 0.19028871809127856, 0.03680426269356685, 0.4815978686532166, 0.10037441644927525, 0.4498127917753624, 0.89274484890771, 0.053627575546145, 0.978515087134343, 0.010742456432828507, 0.20529555933516153, 0.28918949607859473, 0.006931809031468116, 0.23787338259799398, 0.12377940040549276, 0.31886531079482827, 0.03899136262322033, 0.23187362537040096, 0.009536247529710598, 0.1331671229413703, 0.05305219170121682, 0.34680797980991107, 0.10045802007411446, 0.21659962318998252, 0.04945106556854055, 0.12882980796205154, 0.010254635872924515, 0.3609534080189222, 0.010301903643423904, 0.055719565072371954, 0.5055149445862437, 0.20529555933516153, 0.755194808370538, 0.006931809031468116, 0.557355288799679, 0.12377940040549276, 0.7291350120063786, 0.03899136262322033, 0.857296629528919, 0.009536247529710598, 0.6001398284888722, 0.05305219170121682, 0.6829423567359031, 0.10045802007411446, 0.8217191264694079, 0.04945106556854055, 0.6287919561081533, 0.010254635872924515, 0.9339785312842042, 0.010301903643423904, 0.28918949607859473, 0.5055149445862437, 0.23787338259799398, 0.755194808370538, 0.31886531079482827, 0.557355288799679, 0.23187362537040096, 0.7291350120063786, 0.1331671229413703, 0.857296629528919, 0.34680797980991107, 0.6001398284888722, 0.21659962318998252, 0.6829423567359031, 0.12882980796205154, 0.8217191264694079, 0.3609534080189222, 0.6287919561081533, 0.055719565072371954, 0.9339785312842042, 0.28918949607859473, 0.20529555933516153, 0.23787338259799398, 0.006931809031468116, 0.31886531079482827, 0.12377940040549276, 0.23187362537040096, 0.03899136262322033, 0.1331671229413703, 0.009536247529710598, 0.34680797980991107, 0.05305219170121682, 0.21659962318998252, 0.10045802007411446, 0.12882980796205154, 0.04945106556854055, 0.3609534080189222, 0.010254635872924515, 0.055719565072371954, 0.010301903643423904, 0.5055149445862437, 0.28918949607859473, 0.755194808370538, 0.23787338259799398, 0.557355288799679, 0.31886531079482827, 0.7291350120063786, 0.23187362537040096, 0.857296629528919, 0.1331671229413703, 0.6001398284888722, 0.34680797980991107, 0.6829423567359031, 0.21659962318998252, 0.8217191264694079, 0.12882980796205154, 0.6287919561081533, 0.3609534080189222, 0.9339785312842042, 0.055719565072371954, 0.20529555933516153, 0.5055149445862437, 0.006931809031468116, 0.755194808370538, 0.12377940040549276, 0.557355288799679, 0.03899136262322033, 0.7291350120063786, 0.009536247529710598, 0.857296629528919, 0.05305219170121682, 0.6001398284888722, 0.10045802007411446, 0.6829423567359031, 0.04945106556854055, 0.8217191264694079, 0.010254635872924515, 0.6287919561081533, 0.010301903643423904, 0.9339785312842042}; std::vector w = { 0.01072556096456617, 0.0022189148485329394, 0.011500352326641932, 0.006828016226115099, 0.009727620930375354, 0.006107205081692191, 0.009807237613912011, 0.0035760425506418257, 0.0007543496361893447, 0.01072556096456617, 0.0022189148485329394, 0.011500352326641932, 0.006828016226115099, 0.009727620930375354, 0.006107205081692191, 0.009807237613912011, 0.0035760425506418257, 0.0007543496361893447, 0.01072556096456617, 0.0022189148485329394, 0.011500352326641932, 0.006828016226115099, 0.009727620930375354, 0.006107205081692191, 0.009807237613912011, 0.0035760425506418257, 0.0007543496361893447, 0.008747708077881562, 0.002103060144074865, 0.009223742423966418, 0.005234952092662423, 0.002240406560950738, 0.007250152959485511, 0.007952018352713986, 0.004905985911275205, 0.0034199424289671526, 0.0016327142920220426, 0.008747708077881562, 0.002103060144074865, 0.009223742423966418, 0.005234952092662423, 0.002240406560950738, 0.007250152959485511, 0.007952018352713986, 0.004905985911275205, 0.0034199424289671526, 0.0016327142920220426, 0.008747708077881562, 0.002103060144074865, 0.009223742423966418, 0.005234952092662423, 0.002240406560950738, 0.007250152959485511, 0.007952018352713986, 0.004905985911275205, 0.0034199424289671526, 0.0016327142920220426, 0.008747708077881562, 0.002103060144074865, 0.009223742423966418, 0.005234952092662423, 0.002240406560950738, 0.007250152959485511, 0.007952018352713986, 0.004905985911275205, 0.0034199424289671526, 0.0016327142920220426, 0.008747708077881562, 0.002103060144074865, 0.009223742423966418, 0.005234952092662423, 0.002240406560950738, 0.007250152959485511, 0.007952018352713986, 0.004905985911275205, 0.0034199424289671526, 0.0016327142920220426, 0.008747708077881562, 0.002103060144074865, 0.009223742423966418, 0.005234952092662423, 0.002240406560950738, 0.007250152959485511, 0.007952018352713986, 0.004905985911275205, 0.0034199424289671526, 0.0016327142920220426}; return {std::move(x), std::move(w)}; } else if (m == 22) { // Xiao Gimbutas, 3 points, degree 22 std::vector x = {0.3851845246273021, 0.3851845246273021, 0.4577694113676721, 0.4577694113676721, 0.29455825902995014, 0.29455825902995014, 0.18851052363028398, 0.18851052363028398, 0.42198188879353493, 0.42198188879353493, 0.49616117840970864, 0.49616117840970864, 0.029108470670807574, 0.029108470670807574, 0.11543153821920499, 0.11543153821920499, 0.3851845246273021, 0.22963095074539575, 0.4577694113676721, 0.08446117726465585, 0.29455825902995014, 0.4108834819400997, 0.18851052363028398, 0.622978952739432, 0.42198188879353493, 0.15603622241293014, 0.49616117840970864, 0.007677643180582727, 0.029108470670807574, 0.9417830586583849, 0.11543153821920499, 0.76913692356159, 0.22963095074539575, 0.3851845246273021, 0.08446117726465585, 0.4577694113676721, 0.4108834819400997, 0.29455825902995014, 0.622978952739432, 0.18851052363028398, 0.15603622241293014, 0.42198188879353493, 0.007677643180582727, 0.49616117840970864, 0.9417830586583849, 0.029108470670807574, 0.76913692356159, 0.11543153821920499, 0.007876282221582374, 0.06984216946744362, 0.04475228434833587, 0.09039883116640775, 0.038275234700863824, 0.4113417640205587, 0.10274707598693139, 0.3321061050074464, 0.007400241234710751, 0.36257628043246726, 0.19108129796672008, 0.29006682411666884, 0.04399164539345585, 0.28793180282417186, 0.10868994186267199, 0.21678693336494115, 0.009144711374964054, 0.14587371987352518, 0.048254924114641384, 0.17629743482450005, 0.009163909248185229, 0.24399064603949305, 0.0017984649889483744, 0.017934321052938986, 0.9222815483109741, 0.007876282221582374, 0.8648488844852563, 0.04475228434833587, 0.5503830012785775, 0.038275234700863824, 0.5651468190056222, 0.10274707598693139, 0.630023478332822, 0.007400241234710751, 0.5188518779166111, 0.19108129796672008, 0.6680765517823722, 0.04399164539345585, 0.6745231247723869, 0.10868994186267199, 0.8449815687515108, 0.009144711374964054, 0.7754476410608586, 0.048254924114641384, 0.7468454447123217, 0.009163909248185229, 0.9802672139581126, 0.0017984649889483744, 0.06984216946744362, 0.9222815483109741, 0.09039883116640775, 0.8648488844852563, 0.4113417640205587, 0.5503830012785775, 0.3321061050074464, 0.5651468190056222, 0.36257628043246726, 0.630023478332822, 0.29006682411666884, 0.5188518779166111, 0.28793180282417186, 0.6680765517823722, 0.21678693336494115, 0.6745231247723869, 0.14587371987352518, 0.8449815687515108, 0.17629743482450005, 0.7754476410608586, 0.24399064603949305, 0.7468454447123217, 0.017934321052938986, 0.9802672139581126, 0.06984216946744362, 0.007876282221582374, 0.09039883116640775, 0.04475228434833587, 0.4113417640205587, 0.038275234700863824, 0.3321061050074464, 0.10274707598693139, 0.36257628043246726, 0.007400241234710751, 0.29006682411666884, 0.19108129796672008, 0.28793180282417186, 0.04399164539345585, 0.21678693336494115, 0.10868994186267199, 0.14587371987352518, 0.009144711374964054, 0.17629743482450005, 0.048254924114641384, 0.24399064603949305, 0.009163909248185229, 0.017934321052938986, 0.0017984649889483744, 0.9222815483109741, 0.06984216946744362, 0.8648488844852563, 0.09039883116640775, 0.5503830012785775, 0.4113417640205587, 0.5651468190056222, 0.3321061050074464, 0.630023478332822, 0.36257628043246726, 0.5188518779166111, 0.29006682411666884, 0.6680765517823722, 0.28793180282417186, 0.6745231247723869, 0.21678693336494115, 0.8449815687515108, 0.14587371987352518, 0.7754476410608586, 0.17629743482450005, 0.7468454447123217, 0.24399064603949305, 0.9802672139581126, 0.017934321052938986, 0.007876282221582374, 0.9222815483109741, 0.04475228434833587, 0.8648488844852563, 0.038275234700863824, 0.5503830012785775, 0.10274707598693139, 0.5651468190056222, 0.007400241234710751, 0.630023478332822, 0.19108129796672008, 0.5188518779166111, 0.04399164539345585, 0.6680765517823722, 0.10868994186267199, 0.6745231247723869, 0.009144711374964054, 0.8449815687515108, 0.048254924114641384, 0.7754476410608586, 0.009163909248185229, 0.7468454447123217, 0.0017984649889483744, 0.9802672139581126}; std::vector w = { 0.006746541941805331, 0.006930699762117096, 0.010537881978726092, 0.008010649562574445, 0.009426546276920644, 0.002644669832992209, 0.0017845545829281882, 0.007207856564052301, 0.006746541941805331, 0.006930699762117096, 0.010537881978726092, 0.008010649562574445, 0.009426546276920644, 0.002644669832992209, 0.0017845545829281882, 0.007207856564052301, 0.006746541941805331, 0.006930699762117096, 0.010537881978726092, 0.008010649562574445, 0.009426546276920644, 0.002644669832992209, 0.0017845545829281882, 0.007207856564052301, 0.0012977192371156389, 0.003758788908894188, 0.005598656735981385, 0.00885954674475511, 0.0024521301987784822, 0.01085320977775448, 0.005831111433671501, 0.007855081311285159, 0.002053343535787778, 0.005281792483873449, 0.0025270384487923007, 0.0003202142655857129, 0.0012977192371156389, 0.003758788908894188, 0.005598656735981385, 0.00885954674475511, 0.0024521301987784822, 0.01085320977775448, 0.005831111433671501, 0.007855081311285159, 0.002053343535787778, 0.005281792483873449, 0.0025270384487923007, 0.0003202142655857129, 0.0012977192371156389, 0.003758788908894188, 0.005598656735981385, 0.00885954674475511, 0.0024521301987784822, 0.01085320977775448, 0.005831111433671501, 0.007855081311285159, 0.002053343535787778, 0.005281792483873449, 0.0025270384487923007, 0.0003202142655857129, 0.0012977192371156389, 0.003758788908894188, 0.005598656735981385, 0.00885954674475511, 0.0024521301987784822, 0.01085320977775448, 0.005831111433671501, 0.007855081311285159, 0.002053343535787778, 0.005281792483873449, 0.0025270384487923007, 0.0003202142655857129, 0.0012977192371156389, 0.003758788908894188, 0.005598656735981385, 0.00885954674475511, 0.0024521301987784822, 0.01085320977775448, 0.005831111433671501, 0.007855081311285159, 0.002053343535787778, 0.005281792483873449, 0.0025270384487923007, 0.0003202142655857129, 0.0012977192371156389, 0.003758788908894188, 0.005598656735981385, 0.00885954674475511, 0.0024521301987784822, 0.01085320977775448, 0.005831111433671501, 0.007855081311285159, 0.002053343535787778, 0.005281792483873449, 0.0025270384487923007, 0.0003202142655857129}; return {std::move(x), std::move(w)}; } else if (m == 23) { // Xiao Gimbutas, 3 points, degree 23 std::vector x = {0.3333333333333333, 0.3333333333333333, 0.0390072687570322, 0.0390072687570322, 0.4803288773373085, 0.4803288773373085, 0.08684104820763322, 0.08684104820763322, 0.39432350601154154, 0.39432350601154154, 0.2662513178772473, 0.2662513178772473, 0.1371293873116477, 0.1371293873116477, 0.4989594312095863, 0.4989594312095863, 0.4446924421277275, 0.4446924421277275, 0.19874980639653628, 0.19874980639653628, 0.009016440205598442, 0.009016440205598442, 0.0390072687570322, 0.9219854624859356, 0.4803288773373085, 0.039342245325382996, 0.08684104820763322, 0.8263179035847336, 0.39432350601154154, 0.21135298797691693, 0.2662513178772473, 0.46749736424550536, 0.1371293873116477, 0.7257412253767046, 0.4989594312095863, 0.002081137580827397, 0.4446924421277275, 0.11061511574454497, 0.19874980639653628, 0.6025003872069274, 0.009016440205598442, 0.9819671195888031, 0.9219854624859356, 0.0390072687570322, 0.039342245325382996, 0.4803288773373085, 0.8263179035847336, 0.08684104820763322, 0.21135298797691693, 0.39432350601154154, 0.46749736424550536, 0.2662513178772473, 0.7257412253767046, 0.1371293873116477, 0.002081137580827397, 0.4989594312095863, 0.11061511574454497, 0.4446924421277275, 0.6025003872069274, 0.19874980639653628, 0.9819671195888031, 0.009016440205598442, 0.02387025365435361, 0.15950379892475722, 0.005189821760844536, 0.11410136032236454, 0.0327410291887064, 0.0955398781717349, 0.0024475998559663793, 0.31116226805170194, 0.008725289585308535, 0.20561723205805207, 0.007162539910244482, 0.0472616294497253, 0.068526954187213, 0.3585095935696251, 0.10172832932728422, 0.2404827720350127, 0.05835157523751544, 0.17293230312922397, 0.1548301554055162, 0.3163043076538381, 0.014758969729945169, 0.39775857680300764, 0.03299370819253279, 0.27879416981410227, 0.8166259474208892, 0.02387025365435361, 0.880708817916791, 0.005189821760844536, 0.8717190926395587, 0.0327410291887064, 0.6863901320923316, 0.0024475998559663793, 0.7856574783566395, 0.008725289585308535, 0.9455758306400301, 0.007162539910244482, 0.5729634522431619, 0.068526954187213, 0.6577888986377031, 0.10172832932728422, 0.7687161216332605, 0.05835157523751544, 0.5288655369406456, 0.1548301554055162, 0.5874824534670472, 0.014758969729945169, 0.688212121993365, 0.03299370819253279, 0.15950379892475722, 0.8166259474208892, 0.11410136032236454, 0.880708817916791, 0.0955398781717349, 0.8717190926395587, 0.31116226805170194, 0.6863901320923316, 0.20561723205805207, 0.7856574783566395, 0.0472616294497253, 0.9455758306400301, 0.3585095935696251, 0.5729634522431619, 0.2404827720350127, 0.6577888986377031, 0.17293230312922397, 0.7687161216332605, 0.3163043076538381, 0.5288655369406456, 0.39775857680300764, 0.5874824534670472, 0.27879416981410227, 0.688212121993365, 0.15950379892475722, 0.02387025365435361, 0.11410136032236454, 0.005189821760844536, 0.0955398781717349, 0.0327410291887064, 0.31116226805170194, 0.0024475998559663793, 0.20561723205805207, 0.008725289585308535, 0.0472616294497253, 0.007162539910244482, 0.3585095935696251, 0.068526954187213, 0.2404827720350127, 0.10172832932728422, 0.17293230312922397, 0.05835157523751544, 0.3163043076538381, 0.1548301554055162, 0.39775857680300764, 0.014758969729945169, 0.27879416981410227, 0.03299370819253279, 0.8166259474208892, 0.15950379892475722, 0.880708817916791, 0.11410136032236454, 0.8717190926395587, 0.0955398781717349, 0.6863901320923316, 0.31116226805170194, 0.7856574783566395, 0.20561723205805207, 0.9455758306400301, 0.0472616294497253, 0.5729634522431619, 0.3585095935696251, 0.6577888986377031, 0.2404827720350127, 0.7687161216332605, 0.17293230312922397, 0.5288655369406456, 0.3163043076538381, 0.5874824534670472, 0.39775857680300764, 0.688212121993365, 0.27879416981410227, 0.02387025365435361, 0.8166259474208892, 0.005189821760844536, 0.880708817916791, 0.0327410291887064, 0.8717190926395587, 0.0024475998559663793, 0.6863901320923316, 0.008725289585308535, 0.7856574783566395, 0.007162539910244482, 0.9455758306400301, 0.068526954187213, 0.5729634522431619, 0.10172832932728422, 0.6577888986377031, 0.05835157523751544, 0.7687161216332605, 0.1548301554055162, 0.5288655369406456, 0.014758969729945169, 0.5874824534670472, 0.03299370819253279, 0.688212121993365}; std::vector w = { 0.012626530161518105, 0.001957870129516468, 0.00569894463390038, 0.004479958512756771, 0.011837304231564011, 0.011903931443749882, 0.007279724696370875, 0.0012037723020907048, 0.009475975334669443, 0.009967638940052512, 0.0005326806164146575, 0.001957870129516468, 0.00569894463390038, 0.004479958512756771, 0.011837304231564011, 0.011903931443749882, 0.007279724696370875, 0.0012037723020907048, 0.009475975334669443, 0.009967638940052512, 0.0005326806164146575, 0.001957870129516468, 0.00569894463390038, 0.004479958512756771, 0.011837304231564011, 0.011903931443749882, 0.007279724696370875, 0.0012037723020907048, 0.009475975334669443, 0.009967638940052512, 0.0005326806164146575, 0.0012640830276911316, 0.0011125098648622574, 0.0026640152155973924, 0.0011405518381279172, 0.002057375172208046, 0.0009762956639453631, 0.007490556696599583, 0.008060620818508576, 0.0052351282465650335, 0.010422197929484405, 0.0035488894172609124, 0.005087787328353519, 0.0012640830276911316, 0.0011125098648622574, 0.0026640152155973924, 0.0011405518381279172, 0.002057375172208046, 0.0009762956639453631, 0.007490556696599583, 0.008060620818508576, 0.0052351282465650335, 0.010422197929484405, 0.0035488894172609124, 0.005087787328353519, 0.0012640830276911316, 0.0011125098648622574, 0.0026640152155973924, 0.0011405518381279172, 0.002057375172208046, 0.0009762956639453631, 0.007490556696599583, 0.008060620818508576, 0.0052351282465650335, 0.010422197929484405, 0.0035488894172609124, 0.005087787328353519, 0.0012640830276911316, 0.0011125098648622574, 0.0026640152155973924, 0.0011405518381279172, 0.002057375172208046, 0.0009762956639453631, 0.007490556696599583, 0.008060620818508576, 0.0052351282465650335, 0.010422197929484405, 0.0035488894172609124, 0.005087787328353519, 0.0012640830276911316, 0.0011125098648622574, 0.0026640152155973924, 0.0011405518381279172, 0.002057375172208046, 0.0009762956639453631, 0.007490556696599583, 0.008060620818508576, 0.0052351282465650335, 0.010422197929484405, 0.0035488894172609124, 0.005087787328353519, 0.0012640830276911316, 0.0011125098648622574, 0.0026640152155973924, 0.0011405518381279172, 0.002057375172208046, 0.0009762956639453631, 0.007490556696599583, 0.008060620818508576, 0.0052351282465650335, 0.010422197929484405, 0.0035488894172609124, 0.005087787328353519}; return {std::move(x), std::move(w)}; } else if (m == 24) { // Xiao Gimbutas, 3 points, degree 24 std::vector x = {0.3333333333333333, 0.3333333333333333, 0.4188909749106028, 0.4188909749106028, 0.16236063371692644, 0.16236063371692644, 0.04098562900111713, 0.04098562900111713, 0.006731270887888441, 0.006731270887888441, 0.49625527767573513, 0.49625527767573513, 0.2642313154382726, 0.2642313154382726, 0.4806125617925032, 0.4806125617925032, 0.0963284955992153, 0.0963284955992153, 0.3753529267020863, 0.3753529267020863, 0.4188909749106028, 0.16221805017879443, 0.16236063371692644, 0.6752787325661471, 0.04098562900111713, 0.9180287419977657, 0.006731270887888441, 0.9865374582242231, 0.49625527767573513, 0.007489444648529742, 0.2642313154382726, 0.4715373691234548, 0.4806125617925032, 0.03877487641499355, 0.0963284955992153, 0.8073430088015694, 0.3753529267020863, 0.24929414659582738, 0.16221805017879443, 0.4188909749106028, 0.6752787325661471, 0.16236063371692644, 0.9180287419977657, 0.04098562900111713, 0.9865374582242231, 0.006731270887888441, 0.007489444648529742, 0.49625527767573513, 0.4715373691234548, 0.2642313154382726, 0.03877487641499355, 0.4806125617925032, 0.8073430088015694, 0.0963284955992153, 0.24929414659582738, 0.3753529267020863, 0.17036728246244368, 0.241479760073594, 0.169759795860736, 0.3289758089242264, 0.03831822582101938, 0.09316740977988115, 0.09265648152075752, 0.39452027980019433, 0.041188714248475373, 0.16267741639447741, 0.03957090497015804, 0.25358901421887947, 0.038592700174896126, 0.36225224131779127, 0.09453496173659899, 0.28162257770616084, 0.007387994632294238, 0.3832726649926592, 0.007546003162312815, 0.2737503525162605, 0.007234558457782137, 0.09412134279736603, 0.09556626952736523, 0.18039615188676572, 0.007987921880847964, 0.17473734628280568, 0.008074910870208776, 0.03729147205129122, 0.5881529574639623, 0.17036728246244368, 0.5012643952150376, 0.169759795860736, 0.8685143643990995, 0.03831822582101938, 0.5128232386790481, 0.09265648152075752, 0.7961338693570472, 0.041188714248475373, 0.7068400808109625, 0.03957090497015804, 0.5991550585073127, 0.038592700174896126, 0.6238424605572401, 0.09453496173659899, 0.6093393403750466, 0.007387994632294238, 0.7187036443214267, 0.007546003162312815, 0.8986440987448518, 0.007234558457782137, 0.724037578585869, 0.09556626952736523, 0.8172747318363464, 0.007987921880847964, 0.9546336170785, 0.008074910870208776, 0.241479760073594, 0.5881529574639623, 0.3289758089242264, 0.5012643952150376, 0.09316740977988115, 0.8685143643990995, 0.39452027980019433, 0.5128232386790481, 0.16267741639447741, 0.7961338693570472, 0.25358901421887947, 0.7068400808109625, 0.36225224131779127, 0.5991550585073127, 0.28162257770616084, 0.6238424605572401, 0.3832726649926592, 0.6093393403750466, 0.2737503525162605, 0.7187036443214267, 0.09412134279736603, 0.8986440987448518, 0.18039615188676572, 0.724037578585869, 0.17473734628280568, 0.8172747318363464, 0.03729147205129122, 0.9546336170785, 0.241479760073594, 0.17036728246244368, 0.3289758089242264, 0.169759795860736, 0.09316740977988115, 0.03831822582101938, 0.39452027980019433, 0.09265648152075752, 0.16267741639447741, 0.041188714248475373, 0.25358901421887947, 0.03957090497015804, 0.36225224131779127, 0.038592700174896126, 0.28162257770616084, 0.09453496173659899, 0.3832726649926592, 0.007387994632294238, 0.2737503525162605, 0.007546003162312815, 0.09412134279736603, 0.007234558457782137, 0.18039615188676572, 0.09556626952736523, 0.17473734628280568, 0.007987921880847964, 0.03729147205129122, 0.008074910870208776, 0.5881529574639623, 0.241479760073594, 0.5012643952150376, 0.3289758089242264, 0.8685143643990995, 0.09316740977988115, 0.5128232386790481, 0.39452027980019433, 0.7961338693570472, 0.16267741639447741, 0.7068400808109625, 0.25358901421887947, 0.5991550585073127, 0.36225224131779127, 0.6238424605572401, 0.28162257770616084, 0.6093393403750466, 0.3832726649926592, 0.7187036443214267, 0.2737503525162605, 0.8986440987448518, 0.09412134279736603, 0.724037578585869, 0.18039615188676572, 0.8172747318363464, 0.17473734628280568, 0.9546336170785, 0.03729147205129122, 0.17036728246244368, 0.5881529574639623, 0.169759795860736, 0.5012643952150376, 0.03831822582101938, 0.8685143643990995, 0.09265648152075752, 0.5128232386790481, 0.041188714248475373, 0.7961338693570472, 0.03957090497015804, 0.7068400808109625, 0.038592700174896126, 0.5991550585073127, 0.09453496173659899, 0.6238424605572401, 0.007387994632294238, 0.6093393403750466, 0.007546003162312815, 0.7187036443214267, 0.007234558457782137, 0.8986440987448518, 0.09556626952736523, 0.724037578585869, 0.007987921880847964, 0.8172747318363464, 0.008074910870208776, 0.9546336170785}; std::vector w = { 0.00627284492280016, 0.006555266350942619, 0.0051895080282000966, 0.0019168498654645917, 0.0003086272527483216, 0.002171623361085349, 0.010260004335754922, 0.005176247385426301, 0.005013696533694453, 0.009497293258676329, 0.006555266350942619, 0.0051895080282000966, 0.0019168498654645917, 0.0003086272527483216, 0.002171623361085349, 0.010260004335754922, 0.005176247385426301, 0.005013696533694453, 0.009497293258676329, 0.006555266350942619, 0.0051895080282000966, 0.0019168498654645917, 0.0003086272527483216, 0.002171623361085349, 0.010260004335754922, 0.005176247385426301, 0.005013696533694453, 0.009497293258676329, 0.007072522903242423, 0.007637221300662313, 0.002683135727083884, 0.0075159271748706695, 0.0036020670873987137, 0.004452438464081782, 0.004973625937841209, 0.007176175789078727, 0.0021210746334018845, 0.0020406375385582255, 0.0012946061911989924, 0.005921781071271555, 0.0018536133821231541, 0.0008984737927232883, 0.007072522903242423, 0.007637221300662313, 0.002683135727083884, 0.0075159271748706695, 0.0036020670873987137, 0.004452438464081782, 0.004973625937841209, 0.007176175789078727, 0.0021210746334018845, 0.0020406375385582255, 0.0012946061911989924, 0.005921781071271555, 0.0018536133821231541, 0.0008984737927232883, 0.007072522903242423, 0.007637221300662313, 0.002683135727083884, 0.0075159271748706695, 0.0036020670873987137, 0.004452438464081782, 0.004973625937841209, 0.007176175789078727, 0.0021210746334018845, 0.0020406375385582255, 0.0012946061911989924, 0.005921781071271555, 0.0018536133821231541, 0.0008984737927232883, 0.007072522903242423, 0.007637221300662313, 0.002683135727083884, 0.0075159271748706695, 0.0036020670873987137, 0.004452438464081782, 0.004973625937841209, 0.007176175789078727, 0.0021210746334018845, 0.0020406375385582255, 0.0012946061911989924, 0.005921781071271555, 0.0018536133821231541, 0.0008984737927232883, 0.007072522903242423, 0.007637221300662313, 0.002683135727083884, 0.0075159271748706695, 0.0036020670873987137, 0.004452438464081782, 0.004973625937841209, 0.007176175789078727, 0.0021210746334018845, 0.0020406375385582255, 0.0012946061911989924, 0.005921781071271555, 0.0018536133821231541, 0.0008984737927232883, 0.007072522903242423, 0.007637221300662313, 0.002683135727083884, 0.0075159271748706695, 0.0036020670873987137, 0.004452438464081782, 0.004973625937841209, 0.007176175789078727, 0.0021210746334018845, 0.0020406375385582255, 0.0012946061911989924, 0.005921781071271555, 0.0018536133821231541, 0.0008984737927232883}; return {std::move(x), std::move(w)}; } else if (m == 25) { // Xiao Gimbutas, 3 points, degree 25 std::vector x = { 0.3876420304045634, 0.3876420304045634, 0.21100450806149668, 0.21100450806149668, 0.2994923158045085, 0.2994923158045085, 0.03722292599244087, 0.03722292599244087, 0.1451092435745004, 0.1451092435745004, 0.42475930454057476, 0.42475930454057476, 0.4622087087487061, 0.4622087087487061, 0.09294970170076994, 0.09294970170076994, 0.007835344282603851, 0.007835344282603851, 0.48903936966039546, 0.48903936966039546, 0.3876420304045634, 0.22471593919087318, 0.21100450806149668, 0.5779909838770066, 0.2994923158045085, 0.40101536839098295, 0.03722292599244087, 0.9255541480151183, 0.1451092435745004, 0.7097815128509992, 0.42475930454057476, 0.1504813909188505, 0.4622087087487061, 0.07558258250258776, 0.09294970170076994, 0.8141005965984601, 0.007835344282603851, 0.9843293114347923, 0.48903936966039546, 0.021921260679209076, 0.22471593919087318, 0.3876420304045634, 0.5779909838770066, 0.21100450806149668, 0.40101536839098295, 0.2994923158045085, 0.9255541480151183, 0.03722292599244087, 0.7097815128509992, 0.1451092435745004, 0.1504813909188505, 0.42475930454057476, 0.07558258250258776, 0.4622087087487061, 0.8141005965984601, 0.09294970170076994, 0.9843293114347923, 0.007835344282603851, 0.021921260679209076, 0.48903936966039546, 0.0018188666342743875, 0.4404169274793433, 0.03696014157967147, 0.15900790619732788, 0.07885806800563527, 0.1773537967572529, 0.06884752943149791, 0.2700667358209594, 0.11599980764096017, 0.34139103302114987, 0.04831743428737695, 0.3739379797195844, 0.007128314501257424, 0.09913306334168219, 0.20369291058425096, 0.29950641862967453, 0.007236161747948156, 0.17862984860361625, 0.012913883250032529, 0.362068801895972, 0.037687949784259066, 0.08879291548936656, 0.13700669408707095, 0.23362281014171524, 0.02454006024752439, 0.2565954097090198, 0.007188828261693038, 0.041068819111784644, 0.0008914643174981278, 0.2794161886492607, 0.5577642058863823, 0.0018188666342743875, 0.8040319522230007, 0.03696014157967147, 0.7437881352371118, 0.07885806800563527, 0.6610857347475427, 0.06884752943149791, 0.5426091593378899, 0.11599980764096017, 0.5777445859930387, 0.04831743428737695, 0.8937386221570605, 0.007128314501257424, 0.4968006707860745, 0.20369291058425096, 0.8141339896484356, 0.007236161747948156, 0.6250173148539955, 0.012913883250032529, 0.8735191347263744, 0.037687949784259066, 0.6293704957712138, 0.13700669408707095, 0.7188645300434557, 0.02454006024752439, 0.9517423526265223, 0.007188828261693038, 0.7196923470332413, 0.0008914643174981278, 0.4404169274793433, 0.5577642058863823, 0.15900790619732788, 0.8040319522230007, 0.1773537967572529, 0.7437881352371118, 0.2700667358209594, 0.6610857347475427, 0.34139103302114987, 0.5426091593378899, 0.3739379797195844, 0.5777445859930387, 0.09913306334168219, 0.8937386221570605, 0.29950641862967453, 0.4968006707860745, 0.17862984860361625, 0.8141339896484356, 0.362068801895972, 0.6250173148539955, 0.08879291548936656, 0.8735191347263744, 0.23362281014171524, 0.6293704957712138, 0.2565954097090198, 0.7188645300434557, 0.041068819111784644, 0.9517423526265223, 0.2794161886492607, 0.7196923470332413, 0.4404169274793433, 0.0018188666342743875, 0.15900790619732788, 0.03696014157967147, 0.1773537967572529, 0.07885806800563527, 0.2700667358209594, 0.06884752943149791, 0.34139103302114987, 0.11599980764096017, 0.3739379797195844, 0.04831743428737695, 0.09913306334168219, 0.007128314501257424, 0.29950641862967453, 0.20369291058425096, 0.17862984860361625, 0.007236161747948156, 0.362068801895972, 0.012913883250032529, 0.08879291548936656, 0.037687949784259066, 0.23362281014171524, 0.13700669408707095, 0.2565954097090198, 0.02454006024752439, 0.041068819111784644, 0.007188828261693038, 0.2794161886492607, 0.0008914643174981278, 0.5577642058863823, 0.4404169274793433, 0.8040319522230007, 0.15900790619732788, 0.7437881352371118, 0.1773537967572529, 0.6610857347475427, 0.2700667358209594, 0.5426091593378899, 0.34139103302114987, 0.5777445859930387, 0.3739379797195844, 0.8937386221570605, 0.09913306334168219, 0.4968006707860745, 0.29950641862967453, 0.8141339896484356, 0.17862984860361625, 0.6250173148539955, 0.362068801895972, 0.8735191347263744, 0.08879291548936656, 0.6293704957712138, 0.23362281014171524, 0.7188645300434557, 0.2565954097090198, 0.9517423526265223, 0.041068819111784644, 0.7196923470332413, 0.2794161886492607, 0.0018188666342743875, 0.5577642058863823, 0.03696014157967147, 0.8040319522230007, 0.07885806800563527, 0.7437881352371118, 0.06884752943149791, 0.6610857347475427, 0.11599980764096017, 0.5426091593378899, 0.04831743428737695, 0.5777445859930387, 0.007128314501257424, 0.8937386221570605, 0.20369291058425096, 0.4968006707860745, 0.007236161747948156, 0.8141339896484356, 0.012913883250032529, 0.6250173148539955, 0.037687949784259066, 0.8735191347263744, 0.13700669408707095, 0.6293704957712138, 0.02454006024752439, 0.7188645300434557, 0.007188828261693038, 0.9517423526265223, 0.0008914643174981278, 0.7196923470332413}; std::vector w = { 0.006844925774136122, 0.005793631618005297, 0.009008820350850738, 0.001698648860952368, 0.005745762931282399, 0.00795565506872921, 0.006827137593764007, 0.004591410629910018, 0.0004032551441623084, 0.004222042973260539, 0.006844925774136122, 0.005793631618005297, 0.009008820350850738, 0.001698648860952368, 0.005745762931282399, 0.00795565506872921, 0.006827137593764007, 0.004591410629910018, 0.0004032551441623084, 0.004222042973260539, 0.006844925774136122, 0.005793631618005297, 0.009008820350850738, 0.001698648860952368, 0.005745762931282399, 0.00795565506872921, 0.006827137593764007, 0.004591410629910018, 0.0004032551441623084, 0.004222042973260539, 0.0008374089159673526, 0.003155739012379637, 0.004757510783727886, 0.00544219680621846, 0.007920176143949218, 0.0053200853477543926, 0.0012726358126745072, 0.00895691044613803, 0.0016318698410246215, 0.0027273191839872145, 0.00263628096071471, 0.006870041296011276, 0.0036571704539664234, 0.0008464918170636662, 0.0007558510392294402, 0.0008374089159673526, 0.003155739012379637, 0.004757510783727886, 0.00544219680621846, 0.007920176143949218, 0.0053200853477543926, 0.0012726358126745072, 0.00895691044613803, 0.0016318698410246215, 0.0027273191839872145, 0.00263628096071471, 0.006870041296011276, 0.0036571704539664234, 0.0008464918170636662, 0.0007558510392294402, 0.0008374089159673526, 0.003155739012379637, 0.004757510783727886, 0.00544219680621846, 0.007920176143949218, 0.0053200853477543926, 0.0012726358126745072, 0.00895691044613803, 0.0016318698410246215, 0.0027273191839872145, 0.00263628096071471, 0.006870041296011276, 0.0036571704539664234, 0.0008464918170636662, 0.0007558510392294402, 0.0008374089159673526, 0.003155739012379637, 0.004757510783727886, 0.00544219680621846, 0.007920176143949218, 0.0053200853477543926, 0.0012726358126745072, 0.00895691044613803, 0.0016318698410246215, 0.0027273191839872145, 0.00263628096071471, 0.006870041296011276, 0.0036571704539664234, 0.0008464918170636662, 0.0007558510392294402, 0.0008374089159673526, 0.003155739012379637, 0.004757510783727886, 0.00544219680621846, 0.007920176143949218, 0.0053200853477543926, 0.0012726358126745072, 0.00895691044613803, 0.0016318698410246215, 0.0027273191839872145, 0.00263628096071471, 0.006870041296011276, 0.0036571704539664234, 0.0008464918170636662, 0.0007558510392294402, 0.0008374089159673526, 0.003155739012379637, 0.004757510783727886, 0.00544219680621846, 0.007920176143949218, 0.0053200853477543926, 0.0012726358126745072, 0.00895691044613803, 0.0016318698410246215, 0.0027273191839872145, 0.00263628096071471, 0.006870041296011276, 0.0036571704539664234, 0.0008464918170636662, 0.0007558510392294402}; return {std::move(x), std::move(w)}; } else if (m == 26) { // Xiao Gimbutas, 3 points, degree 26 std::vector x = { 0.3333333333333333, 0.3333333333333333, 0.06673712257646625, 0.06673712257646625, 0.0063401164920769415, 0.0063401164920769415, 0.4937530328963848, 0.4937530328963848, 0.388787497107594, 0.388787497107594, 0.2731471009290788, 0.2731471009290788, 0.471828563321166, 0.471828563321166, 0.1542014303645443, 0.1542014303645443, 0.21204316330220568, 0.21204316330220568, 0.4359854193843832, 0.4359854193843832, 0.06673712257646625, 0.8665257548470675, 0.0063401164920769415, 0.9873197670158461, 0.4937530328963848, 0.01249393420723044, 0.388787497107594, 0.22242500578481195, 0.2731471009290788, 0.4537057981418424, 0.471828563321166, 0.056342873357667966, 0.1542014303645443, 0.6915971392709114, 0.21204316330220568, 0.5759136733955886, 0.4359854193843832, 0.12802916123123365, 0.8665257548470675, 0.06673712257646625, 0.9873197670158461, 0.0063401164920769415, 0.01249393420723044, 0.4937530328963848, 0.22242500578481195, 0.388787497107594, 0.4537057981418424, 0.2731471009290788, 0.056342873357667966, 0.471828563321166, 0.6915971392709114, 0.1542014303645443, 0.5759136733955886, 0.21204316330220568, 0.12802916123123365, 0.4359854193843832, 0.004794660975436677, 0.08007165494031654, 0.029155196206835834, 0.031643611571530776, 0.02620936402249865, 0.07538004751539866, 0.005698117916875216, 0.03310003433603227, 0.041724722742120926, 0.13248618961456732, 0.10004565910652752, 0.10868713291440213, 0.120614402205249, 0.25027231329052646, 0.029537942516907823, 0.3890220620427618, 0.08737846516384448, 0.35850929642766155, 0.07631190151295938, 0.18686917947622156, 0.002057530965370865, 0.4147059095903063, 0.1704787284972489, 0.31941530538343876, 0.007999608091484301, 0.14373762619976402, 0.05116587368513777, 0.2837881388594704, 0.02278459925089566, 0.21654666647347712, 0.009473297912213558, 0.31289850307488, 0.0004640077321756526, 0.22643479740771752, 0.9151336840842468, 0.004794660975436677, 0.9392011922216335, 0.029155196206835834, 0.8984105884621028, 0.02620936402249865, 0.9612018477470925, 0.005698117916875216, 0.8257890876433118, 0.041724722742120926, 0.7912672079790704, 0.10004565910652752, 0.6291132845042245, 0.120614402205249, 0.5814399954403304, 0.029537942516907823, 0.554112238408494, 0.08737846516384448, 0.736818919010819, 0.07631190151295938, 0.5832365594443228, 0.002057530965370865, 0.5101059661193124, 0.1704787284972489, 0.8482627657087517, 0.007999608091484301, 0.6650459874553918, 0.05116587368513777, 0.7606687342756272, 0.02278459925089566, 0.6776281990129065, 0.009473297912213558, 0.7731011948601068, 0.0004640077321756526, 0.08007165494031654, 0.9151336840842468, 0.031643611571530776, 0.9392011922216335, 0.07538004751539866, 0.8984105884621028, 0.03310003433603227, 0.9612018477470925, 0.13248618961456732, 0.8257890876433118, 0.10868713291440213, 0.7912672079790704, 0.25027231329052646, 0.6291132845042245, 0.3890220620427618, 0.5814399954403304, 0.35850929642766155, 0.554112238408494, 0.18686917947622156, 0.736818919010819, 0.4147059095903063, 0.5832365594443228, 0.31941530538343876, 0.5101059661193124, 0.14373762619976402, 0.8482627657087517, 0.2837881388594704, 0.6650459874553918, 0.21654666647347712, 0.7606687342756272, 0.31289850307488, 0.6776281990129065, 0.22643479740771752, 0.7731011948601068, 0.08007165494031654, 0.004794660975436677, 0.031643611571530776, 0.029155196206835834, 0.07538004751539866, 0.02620936402249865, 0.03310003433603227, 0.005698117916875216, 0.13248618961456732, 0.041724722742120926, 0.10868713291440213, 0.10004565910652752, 0.25027231329052646, 0.120614402205249, 0.3890220620427618, 0.029537942516907823, 0.35850929642766155, 0.08737846516384448, 0.18686917947622156, 0.07631190151295938, 0.4147059095903063, 0.002057530965370865, 0.31941530538343876, 0.1704787284972489, 0.14373762619976402, 0.007999608091484301, 0.2837881388594704, 0.05116587368513777, 0.21654666647347712, 0.02278459925089566, 0.31289850307488, 0.009473297912213558, 0.22643479740771752, 0.0004640077321756526, 0.9151336840842468, 0.08007165494031654, 0.9392011922216335, 0.031643611571530776, 0.8984105884621028, 0.07538004751539866, 0.9612018477470925, 0.03310003433603227, 0.8257890876433118, 0.13248618961456732, 0.7912672079790704, 0.10868713291440213, 0.6291132845042245, 0.25027231329052646, 0.5814399954403304, 0.3890220620427618, 0.554112238408494, 0.35850929642766155, 0.736818919010819, 0.18686917947622156, 0.5832365594443228, 0.4147059095903063, 0.5101059661193124, 0.31941530538343876, 0.8482627657087517, 0.14373762619976402, 0.6650459874553918, 0.2837881388594704, 0.7606687342756272, 0.21654666647347712, 0.6776281990129065, 0.31289850307488, 0.7731011948601068, 0.22643479740771752, 0.004794660975436677, 0.9151336840842468, 0.029155196206835834, 0.9392011922216335, 0.02620936402249865, 0.8984105884621028, 0.005698117916875216, 0.9612018477470925, 0.041724722742120926, 0.8257890876433118, 0.10004565910652752, 0.7912672079790704, 0.120614402205249, 0.6291132845042245, 0.029537942516907823, 0.5814399954403304, 0.08737846516384448, 0.554112238408494, 0.07631190151295938, 0.736818919010819, 0.002057530965370865, 0.5832365594443228, 0.1704787284972489, 0.5101059661193124, 0.007999608091484301, 0.8482627657087517, 0.05116587368513777, 0.6650459874553918, 0.02278459925089566, 0.7606687342756272, 0.009473297912213558, 0.6776281990129065, 0.0004640077321756526, 0.7731011948601068}; std::vector w = { 0.010243331294611621, 0.002456912651483009, 0.00026347655834093594, 0.002651079590933673, 0.00973403391859144, 0.00976782346162377, 0.005764251817328446, 0.006627629724272634, 0.008472172539264045, 0.008206200301293952, 0.002456912651483009, 0.00026347655834093594, 0.002651079590933673, 0.00973403391859144, 0.00976782346162377, 0.005764251817328446, 0.006627629724272634, 0.008472172539264045, 0.008206200301293952, 0.002456912651483009, 0.00026347655834093594, 0.002651079590933673, 0.00973403391859144, 0.00976782346162377, 0.005764251817328446, 0.006627629724272634, 0.008472172539264045, 0.008206200301293952, 0.0006992632240801362, 0.0006027823868584428, 0.0016527723564838351, 0.0005428536714983775, 0.0032017989498564093, 0.002307105538189159, 0.00718973661379937, 0.00412988360854342, 0.006863979108042852, 0.005198822764087162, 0.000928573735499042, 0.008799583590347606, 0.0014833808313282528, 0.005053562216044342, 0.0031346689230402846, 0.0022957791936993187, 0.0005697744579341068, 0.0006992632240801362, 0.0006027823868584428, 0.0016527723564838351, 0.0005428536714983775, 0.0032017989498564093, 0.002307105538189159, 0.00718973661379937, 0.00412988360854342, 0.006863979108042852, 0.005198822764087162, 0.000928573735499042, 0.008799583590347606, 0.0014833808313282528, 0.005053562216044342, 0.0031346689230402846, 0.0022957791936993187, 0.0005697744579341068, 0.0006992632240801362, 0.0006027823868584428, 0.0016527723564838351, 0.0005428536714983775, 0.0032017989498564093, 0.002307105538189159, 0.00718973661379937, 0.00412988360854342, 0.006863979108042852, 0.005198822764087162, 0.000928573735499042, 0.008799583590347606, 0.0014833808313282528, 0.005053562216044342, 0.0031346689230402846, 0.0022957791936993187, 0.0005697744579341068, 0.0006992632240801362, 0.0006027823868584428, 0.0016527723564838351, 0.0005428536714983775, 0.0032017989498564093, 0.002307105538189159, 0.00718973661379937, 0.00412988360854342, 0.006863979108042852, 0.005198822764087162, 0.000928573735499042, 0.008799583590347606, 0.0014833808313282528, 0.005053562216044342, 0.0031346689230402846, 0.0022957791936993187, 0.0005697744579341068, 0.0006992632240801362, 0.0006027823868584428, 0.0016527723564838351, 0.0005428536714983775, 0.0032017989498564093, 0.002307105538189159, 0.00718973661379937, 0.00412988360854342, 0.006863979108042852, 0.005198822764087162, 0.000928573735499042, 0.008799583590347606, 0.0014833808313282528, 0.005053562216044342, 0.0031346689230402846, 0.0022957791936993187, 0.0005697744579341068, 0.0006992632240801362, 0.0006027823868584428, 0.0016527723564838351, 0.0005428536714983775, 0.0032017989498564093, 0.002307105538189159, 0.00718973661379937, 0.00412988360854342, 0.006863979108042852, 0.005198822764087162, 0.000928573735499042, 0.008799583590347606, 0.0014833808313282528, 0.005053562216044342, 0.0031346689230402846, 0.0022957791936993187, 0.0005697744579341068}; return {std::move(x), std::move(w)}; } else if (m == 27) { // Xiao Gimbutas, 3 points, degree 27 std::vector x = { 0.3807140211811872, 0.3807140211811872, 0.4466678037038646, 0.4466678037038646, 0.41614137880541213, 0.41614137880541213, 0.08030464778843843, 0.08030464778843843, 0.23340040666987116, 0.23340040666987116, 0.3011654651665092, 0.3011654651665092, 0.17477996635490006, 0.17477996635490006, 0.48556505418516277, 0.48556505418516277, 0.03257152018018172, 0.03257152018018172, 0.12757090190467762, 0.12757090190467762, 0.0066392191809588885, 0.0066392191809588885, 0.3807140211811872, 0.23857195763762562, 0.4466678037038646, 0.10666439259227078, 0.41614137880541213, 0.16771724238917574, 0.08030464778843843, 0.8393907044231231, 0.23340040666987116, 0.5331991866602577, 0.3011654651665092, 0.39766906966698157, 0.17477996635490006, 0.6504400672901999, 0.48556505418516277, 0.02886989162967446, 0.03257152018018172, 0.9348569596396366, 0.12757090190467762, 0.7448581961906448, 0.0066392191809588885, 0.9867215616380822, 0.23857195763762562, 0.3807140211811872, 0.10666439259227078, 0.4466678037038646, 0.16771724238917574, 0.41614137880541213, 0.8393907044231231, 0.08030464778843843, 0.5331991866602577, 0.23340040666987116, 0.39766906966698157, 0.3011654651665092, 0.6504400672901999, 0.17477996635490006, 0.02886989162967446, 0.48556505418516277, 0.9348569596396366, 0.03257152018018172, 0.7448581961906448, 0.12757090190467762, 0.9867215616380822, 0.0066392191809588885, 0.030730604727272855, 0.2870421965934966, 0.12915264006344968, 0.3450878417155684, 0.028033486095250002, 0.3759301570486618, 0.20913092113766868, 0.31694558893313196, 0.06603891284973865, 0.4072283930427199, 0.041030576819181826, 0.21355359845782393, 0.005299640371799034, 0.32885287806889263, 0.06307399541495087, 0.13929530614214874, 0.1489628509382401, 0.25524625469697804, 0.09469708243313069, 0.20837601560037405, 0.005580717015260116, 0.44001055194621547, 0.07507690243319622, 0.3022209412278211, 0.0069825293244590156, 0.08194680258353369, 0.0060935694037648315, 0.03436496991214199, 0.03503442252769738, 0.08011207384710112, 0.019352001318038967, 0.14721343189892247, 0.007332472549040455, 0.22971965325784321, 0.0004903284434629743, 0.1476555211198698, 0.6822271986792305, 0.030730604727272855, 0.5257595182209819, 0.12915264006344968, 0.5960363568560882, 0.028033486095250002, 0.4739234899291994, 0.20913092113766868, 0.5267326941075414, 0.06603891284973865, 0.7454158247229942, 0.041030576819181826, 0.6658474815593083, 0.005299640371799034, 0.7976306984429005, 0.06307399541495087, 0.5957908943647818, 0.1489628509382401, 0.6969269019664952, 0.09469708243313069, 0.5544087310385244, 0.005580717015260116, 0.6227021563389827, 0.07507690243319622, 0.9110706680920073, 0.0069825293244590156, 0.9595414606840932, 0.0060935694037648315, 0.8848535036252014, 0.03503442252769738, 0.8334345667830386, 0.019352001318038967, 0.7629478741931164, 0.007332472549040455, 0.8518541504366672, 0.0004903284434629743, 0.2870421965934966, 0.6822271986792305, 0.3450878417155684, 0.5257595182209819, 0.3759301570486618, 0.5960363568560882, 0.31694558893313196, 0.4739234899291994, 0.4072283930427199, 0.5267326941075414, 0.21355359845782393, 0.7454158247229942, 0.32885287806889263, 0.6658474815593083, 0.13929530614214874, 0.7976306984429005, 0.25524625469697804, 0.5957908943647818, 0.20837601560037405, 0.6969269019664952, 0.44001055194621547, 0.5544087310385244, 0.3022209412278211, 0.6227021563389827, 0.08194680258353369, 0.9110706680920073, 0.03436496991214199, 0.9595414606840932, 0.08011207384710112, 0.8848535036252014, 0.14721343189892247, 0.8334345667830386, 0.22971965325784321, 0.7629478741931164, 0.1476555211198698, 0.8518541504366672, 0.2870421965934966, 0.030730604727272855, 0.3450878417155684, 0.12915264006344968, 0.3759301570486618, 0.028033486095250002, 0.31694558893313196, 0.20913092113766868, 0.4072283930427199, 0.06603891284973865, 0.21355359845782393, 0.041030576819181826, 0.32885287806889263, 0.005299640371799034, 0.13929530614214874, 0.06307399541495087, 0.25524625469697804, 0.1489628509382401, 0.20837601560037405, 0.09469708243313069, 0.44001055194621547, 0.005580717015260116, 0.3022209412278211, 0.07507690243319622, 0.08194680258353369, 0.0069825293244590156, 0.03436496991214199, 0.0060935694037648315, 0.08011207384710112, 0.03503442252769738, 0.14721343189892247, 0.019352001318038967, 0.22971965325784321, 0.007332472549040455, 0.1476555211198698, 0.0004903284434629743, 0.6822271986792305, 0.2870421965934966, 0.5257595182209819, 0.3450878417155684, 0.5960363568560882, 0.3759301570486618, 0.4739234899291994, 0.31694558893313196, 0.5267326941075414, 0.4072283930427199, 0.7454158247229942, 0.21355359845782393, 0.6658474815593083, 0.32885287806889263, 0.7976306984429005, 0.13929530614214874, 0.5957908943647818, 0.25524625469697804, 0.6969269019664952, 0.20837601560037405, 0.5544087310385244, 0.44001055194621547, 0.6227021563389827, 0.3022209412278211, 0.9110706680920073, 0.08194680258353369, 0.9595414606840932, 0.03436496991214199, 0.8848535036252014, 0.08011207384710112, 0.8334345667830386, 0.14721343189892247, 0.7629478741931164, 0.22971965325784321, 0.8518541504366672, 0.1476555211198698, 0.030730604727272855, 0.6822271986792305, 0.12915264006344968, 0.5257595182209819, 0.028033486095250002, 0.5960363568560882, 0.20913092113766868, 0.4739234899291994, 0.06603891284973865, 0.5267326941075414, 0.041030576819181826, 0.7454158247229942, 0.005299640371799034, 0.6658474815593083, 0.06307399541495087, 0.7976306984429005, 0.1489628509382401, 0.5957908943647818, 0.09469708243313069, 0.6969269019664952, 0.005580717015260116, 0.5544087310385244, 0.07507690243319622, 0.6227021563389827, 0.0069825293244590156, 0.9110706680920073, 0.0060935694037648315, 0.9595414606840932, 0.03503442252769738, 0.8848535036252014, 0.019352001318038967, 0.8334345667830386, 0.007332472549040455, 0.7629478741931164, 0.0004903284434629743, 0.8518541504366672}; std::vector w = { 0.00478004248372996, 0.004705079904727113, 0.006025113512075217, 0.0026063109364009383, 0.006735657699024688, 0.007873982890681327, 0.00564122127234919, 0.003558618706437321, 0.0013886697644770905, 0.004871622461408866, 0.0002877212028352512, 0.00478004248372996, 0.004705079904727113, 0.006025113512075217, 0.0026063109364009383, 0.006735657699024688, 0.007873982890681327, 0.00564122127234919, 0.003558618706437321, 0.0013886697644770905, 0.004871622461408866, 0.0002877212028352512, 0.00478004248372996, 0.004705079904727113, 0.006025113512075217, 0.0026063109364009383, 0.006735657699024688, 0.007873982890681327, 0.00564122127234919, 0.003558618706437321, 0.0013886697644770905, 0.004871622461408866, 0.0002877212028352512, 0.0027658974168833657, 0.006278718102018268, 0.0031975763497272013, 0.00685769661527542, 0.004931135059494785, 0.003227686452464845, 0.0014639131808995513, 0.003565317655243512, 0.006173831565430682, 0.005346850294808132, 0.0016212337988196705, 0.005465305546456643, 0.0010075615636448512, 0.0005983868042365814, 0.0021635790176803564, 0.002311193555890555, 0.0016971268694035136, 0.00042330306788192527, 0.0027658974168833657, 0.006278718102018268, 0.0031975763497272013, 0.00685769661527542, 0.004931135059494785, 0.003227686452464845, 0.0014639131808995513, 0.003565317655243512, 0.006173831565430682, 0.005346850294808132, 0.0016212337988196705, 0.005465305546456643, 0.0010075615636448512, 0.0005983868042365814, 0.0021635790176803564, 0.002311193555890555, 0.0016971268694035136, 0.00042330306788192527, 0.0027658974168833657, 0.006278718102018268, 0.0031975763497272013, 0.00685769661527542, 0.004931135059494785, 0.003227686452464845, 0.0014639131808995513, 0.003565317655243512, 0.006173831565430682, 0.005346850294808132, 0.0016212337988196705, 0.005465305546456643, 0.0010075615636448512, 0.0005983868042365814, 0.0021635790176803564, 0.002311193555890555, 0.0016971268694035136, 0.00042330306788192527, 0.0027658974168833657, 0.006278718102018268, 0.0031975763497272013, 0.00685769661527542, 0.004931135059494785, 0.003227686452464845, 0.0014639131808995513, 0.003565317655243512, 0.006173831565430682, 0.005346850294808132, 0.0016212337988196705, 0.005465305546456643, 0.0010075615636448512, 0.0005983868042365814, 0.0021635790176803564, 0.002311193555890555, 0.0016971268694035136, 0.00042330306788192527, 0.0027658974168833657, 0.006278718102018268, 0.0031975763497272013, 0.00685769661527542, 0.004931135059494785, 0.003227686452464845, 0.0014639131808995513, 0.003565317655243512, 0.006173831565430682, 0.005346850294808132, 0.0016212337988196705, 0.005465305546456643, 0.0010075615636448512, 0.0005983868042365814, 0.0021635790176803564, 0.002311193555890555, 0.0016971268694035136, 0.00042330306788192527, 0.0027658974168833657, 0.006278718102018268, 0.0031975763497272013, 0.00685769661527542, 0.004931135059494785, 0.003227686452464845, 0.0014639131808995513, 0.003565317655243512, 0.006173831565430682, 0.005346850294808132, 0.0016212337988196705, 0.005465305546456643, 0.0010075615636448512, 0.0005983868042365814, 0.0021635790176803564, 0.002311193555890555, 0.0016971268694035136, 0.00042330306788192527}; return {std::move(x), std::move(w)}; } else if (m == 28) { // Xiao Gimbutas, 3 points, degree 28 std::vector x = { 0.3039829225164842, 0.3039829225164842, 0.004804126196658098, 0.004804126196658098, 0.45827990424041193, 0.45827990424041193, 0.38626797357004206, 0.38626797357004206, 0.2582640721504622, 0.2582640721504622, 0.10589584417862768, 0.10589584417862768, 0.42955220211889933, 0.42955220211889933, 0.4848411325625894, 0.4848411325625894, 0.15863768886305973, 0.15863768886305973, 0.06083919239275881, 0.06083919239275881, 0.3039829225164842, 0.39203415496703165, 0.004804126196658098, 0.9903917476066838, 0.45827990424041193, 0.08344019151917614, 0.38626797357004206, 0.2274640528599159, 0.2582640721504622, 0.4834718556990756, 0.10589584417862768, 0.7882083116427446, 0.42955220211889933, 0.14089559576220134, 0.4848411325625894, 0.030317734874821145, 0.15863768886305973, 0.6827246222738805, 0.06083919239275881, 0.8783216152144824, 0.39203415496703165, 0.3039829225164842, 0.9903917476066838, 0.004804126196658098, 0.08344019151917614, 0.45827990424041193, 0.2274640528599159, 0.38626797357004206, 0.4834718556990756, 0.2582640721504622, 0.7882083116427446, 0.10589584417862768, 0.14089559576220134, 0.42955220211889933, 0.030317734874821145, 0.4848411325625894, 0.6827246222738805, 0.15863768886305973, 0.8783216152144824, 0.06083919239275881, 0.02152438536945612, 0.0455054005583464, 0.04906966935755949, 0.2133944547670873, 0.17765845029637026, 0.24210251191931964, 0.1898123562927368, 0.32719073201917004, 0.0044583820232893204, 0.14199816693317424, 0.08767797648435202, 0.17539639319146172, 0.06318032763441064, 0.39213961333441455, 0.004149464133923672, 0.33414561503592133, 0.022794804925916238, 0.17414619605118214, 0.022700844371797004, 0.2758390080718242, 0.006149648542663968, 0.026222667164652273, 0.11203362934227094, 0.24304720236592617, 0.004781489772987132, 0.22999298405790716, 0.062448742179632866, 0.29560828087240165, 0.050211185913428096, 0.12139345075409119, 0.025727998742878733, 0.3738238003102097, 0.005646565993466159, 0.44278340652024356, 0.11808906971509502, 0.3377986632005823, 0.018242291012294715, 0.09222918919528221, 0.0012002556014871519, 0.07029074047813273, 0.9329702140721975, 0.02152438536945612, 0.7375358758753532, 0.04906966935755949, 0.5802390377843101, 0.17765845029637026, 0.4829969116880932, 0.1898123562927368, 0.8535434510435365, 0.0044583820232893204, 0.7369256303241862, 0.08767797648435202, 0.5446800590311749, 0.06318032763441064, 0.661704920830155, 0.004149464133923672, 0.8030589990229016, 0.022794804925916238, 0.7014601475563789, 0.022700844371797004, 0.9676276842926838, 0.006149648542663968, 0.644919168291803, 0.11203362934227094, 0.7652255261691058, 0.004781489772987132, 0.6419429769479654, 0.062448742179632866, 0.8283953633324808, 0.050211185913428096, 0.6004482009469116, 0.025727998742878733, 0.5515700274862902, 0.005646565993466159, 0.5441122670843226, 0.11808906971509502, 0.8895285197924231, 0.018242291012294715, 0.92850900392038, 0.0012002556014871519, 0.0455054005583464, 0.9329702140721975, 0.2133944547670873, 0.7375358758753532, 0.24210251191931964, 0.5802390377843101, 0.32719073201917004, 0.4829969116880932, 0.14199816693317424, 0.8535434510435365, 0.17539639319146172, 0.7369256303241862, 0.39213961333441455, 0.5446800590311749, 0.33414561503592133, 0.661704920830155, 0.17414619605118214, 0.8030589990229016, 0.2758390080718242, 0.7014601475563789, 0.026222667164652273, 0.9676276842926838, 0.24304720236592617, 0.644919168291803, 0.22999298405790716, 0.7652255261691058, 0.29560828087240165, 0.6419429769479654, 0.12139345075409119, 0.8283953633324808, 0.3738238003102097, 0.6004482009469116, 0.44278340652024356, 0.5515700274862902, 0.3377986632005823, 0.5441122670843226, 0.09222918919528221, 0.8895285197924231, 0.07029074047813273, 0.92850900392038, 0.0455054005583464, 0.02152438536945612, 0.2133944547670873, 0.04906966935755949, 0.24210251191931964, 0.17765845029637026, 0.32719073201917004, 0.1898123562927368, 0.14199816693317424, 0.0044583820232893204, 0.17539639319146172, 0.08767797648435202, 0.39213961333441455, 0.06318032763441064, 0.33414561503592133, 0.004149464133923672, 0.17414619605118214, 0.022794804925916238, 0.2758390080718242, 0.022700844371797004, 0.026222667164652273, 0.006149648542663968, 0.24304720236592617, 0.11203362934227094, 0.22999298405790716, 0.004781489772987132, 0.29560828087240165, 0.062448742179632866, 0.12139345075409119, 0.050211185913428096, 0.3738238003102097, 0.025727998742878733, 0.44278340652024356, 0.005646565993466159, 0.3377986632005823, 0.11808906971509502, 0.09222918919528221, 0.018242291012294715, 0.07029074047813273, 0.0012002556014871519, 0.9329702140721975, 0.0455054005583464, 0.7375358758753532, 0.2133944547670873, 0.5802390377843101, 0.24210251191931964, 0.4829969116880932, 0.32719073201917004, 0.8535434510435365, 0.14199816693317424, 0.7369256303241862, 0.17539639319146172, 0.5446800590311749, 0.39213961333441455, 0.661704920830155, 0.33414561503592133, 0.8030589990229016, 0.17414619605118214, 0.7014601475563789, 0.2758390080718242, 0.9676276842926838, 0.026222667164652273, 0.644919168291803, 0.24304720236592617, 0.7652255261691058, 0.22999298405790716, 0.6419429769479654, 0.29560828087240165, 0.8283953633324808, 0.12139345075409119, 0.6004482009469116, 0.3738238003102097, 0.5515700274862902, 0.44278340652024356, 0.5441122670843226, 0.3377986632005823, 0.8895285197924231, 0.09222918919528221, 0.92850900392038, 0.07029074047813273, 0.02152438536945612, 0.9329702140721975, 0.04906966935755949, 0.7375358758753532, 0.17765845029637026, 0.5802390377843101, 0.1898123562927368, 0.4829969116880932, 0.0044583820232893204, 0.8535434510435365, 0.08767797648435202, 0.7369256303241862, 0.06318032763441064, 0.5446800590311749, 0.004149464133923672, 0.661704920830155, 0.022794804925916238, 0.8030589990229016, 0.022700844371797004, 0.7014601475563789, 0.006149648542663968, 0.9676276842926838, 0.11203362934227094, 0.644919168291803, 0.004781489772987132, 0.7652255261691058, 0.062448742179632866, 0.6419429769479654, 0.050211185913428096, 0.8283953633324808, 0.025727998742878733, 0.6004482009469116, 0.005646565993466159, 0.5515700274862902, 0.11808906971509502, 0.5441122670843226, 0.018242291012294715, 0.8895285197924231, 0.0012002556014871519, 0.92850900392038}; std::vector w = {0.007181233150323068, 0.00015556760434074816, 0.0044258525054465805, 0.007105293195224093, 0.006546374294544483, 0.0039049716855431705, 0.006773797801662755, 0.003841055289297512, 0.005971334820124114, 0.0025641260023390343, 0.007181233150323068, 0.00015556760434074816, 0.0044258525054465805, 0.007105293195224093, 0.006546374294544483, 0.0039049716855431705, 0.006773797801662755, 0.003841055289297512, 0.005971334820124114, 0.0025641260023390343, 0.007181233150323068, 0.00015556760434074816, 0.0044258525054465805, 0.007105293195224093, 0.006546374294544483, 0.0039049716855431705, 0.006773797801662755, 0.003841055289297512, 0.005971334820124114, 0.0025641260023390343, 0.0010587697788404492, 0.002947836117257123, 0.006278628108338439, 0.006553057062049693, 0.0008894477096277566, 0.004005964643347482, 0.004232347867138302, 0.0011883821338438917, 0.0021384711675089727, 0.0025656386910186484, 0.0004742702129447003, 0.0051229932808521, 0.0011622726822320252, 0.004462558070138275, 0.0029589418861769453, 0.003130267321484276, 0.0015700888217440182, 0.0063935692114779, 0.0016024580953965175, 0.0003625672974930415, 0.0010587697788404492, 0.002947836117257123, 0.006278628108338439, 0.006553057062049693, 0.0008894477096277566, 0.004005964643347482, 0.004232347867138302, 0.0011883821338438917, 0.0021384711675089727, 0.0025656386910186484, 0.0004742702129447003, 0.0051229932808521, 0.0011622726822320252, 0.004462558070138275, 0.0029589418861769453, 0.003130267321484276, 0.0015700888217440182, 0.0063935692114779, 0.0016024580953965175, 0.0003625672974930415, 0.0010587697788404492, 0.002947836117257123, 0.006278628108338439, 0.006553057062049693, 0.0008894477096277566, 0.004005964643347482, 0.004232347867138302, 0.0011883821338438917, 0.0021384711675089727, 0.0025656386910186484, 0.0004742702129447003, 0.0051229932808521, 0.0011622726822320252, 0.004462558070138275, 0.0029589418861769453, 0.003130267321484276, 0.0015700888217440182, 0.0063935692114779, 0.0016024580953965175, 0.0003625672974930415, 0.0010587697788404492, 0.002947836117257123, 0.006278628108338439, 0.006553057062049693, 0.0008894477096277566, 0.004005964643347482, 0.004232347867138302, 0.0011883821338438917, 0.0021384711675089727, 0.0025656386910186484, 0.0004742702129447003, 0.0051229932808521, 0.0011622726822320252, 0.004462558070138275, 0.0029589418861769453, 0.003130267321484276, 0.0015700888217440182, 0.0063935692114779, 0.0016024580953965175, 0.0003625672974930415, 0.0010587697788404492, 0.002947836117257123, 0.006278628108338439, 0.006553057062049693, 0.0008894477096277566, 0.004005964643347482, 0.004232347867138302, 0.0011883821338438917, 0.0021384711675089727, 0.0025656386910186484, 0.0004742702129447003, 0.0051229932808521, 0.0011622726822320252, 0.004462558070138275, 0.0029589418861769453, 0.003130267321484276, 0.0015700888217440182, 0.0063935692114779, 0.0016024580953965175, 0.0003625672974930415, 0.0010587697788404492, 0.002947836117257123, 0.006278628108338439, 0.006553057062049693, 0.0008894477096277566, 0.004005964643347482, 0.004232347867138302, 0.0011883821338438917, 0.0021384711675089727, 0.0025656386910186484, 0.0004742702129447003, 0.0051229932808521, 0.0011622726822320252, 0.004462558070138275, 0.0029589418861769453, 0.003130267321484276, 0.0015700888217440182, 0.0063935692114779, 0.0016024580953965175, 0.0003625672974930415}; return {std::move(x), std::move(w)}; } else if (m == 29) { // Xiao Gimbutas, 3 points, degree 29 std::vector x = { 0.49891482463768616, 0.49891482463768616, 0.4343804267617306, 0.4343804267617306, 0.0410973356271182, 0.0410973356271182, 0.2084053051324009, 0.2084053051324009, 0.16074588443196364, 0.16074588443196364, 0.48840160293260276, 0.48840160293260276, 0.3023864112151285, 0.3023864112151285, 0.11442681299442559, 0.11442681299442559, 0.46476243108073895, 0.46476243108073895, 0.07372188139009989, 0.07372188139009989, 0.39061917878326374, 0.39061917878326374, 0.49891482463768616, 0.0021703507246276788, 0.4343804267617306, 0.13123914647653878, 0.0410973356271182, 0.9178053287457636, 0.2084053051324009, 0.5831893897351982, 0.16074588443196364, 0.6785082311360727, 0.48840160293260276, 0.023196794134794474, 0.3023864112151285, 0.395227177569743, 0.11442681299442559, 0.7711463740111488, 0.46476243108073895, 0.0704751378385221, 0.07372188139009989, 0.8525562372198002, 0.39061917878326374, 0.21876164243347251, 0.0021703507246276788, 0.49891482463768616, 0.13123914647653878, 0.4343804267617306, 0.9178053287457636, 0.0410973356271182, 0.5831893897351982, 0.2084053051324009, 0.6785082311360727, 0.16074588443196364, 0.023196794134794474, 0.48840160293260276, 0.395227177569743, 0.3023864112151285, 0.7711463740111488, 0.11442681299442559, 0.0704751378385221, 0.46476243108073895, 0.8525562372198002, 0.07372188139009989, 0.21876164243347251, 0.39061917878326374, 0.002728743247921069, 0.058942108840229206, 0.15717769986719343, 0.34978801000933185, 0.0021009666448275587, 0.32300182354355017, 0.06816580881374641, 0.15814585424951613, 0.010830958603609348, 0.029549468261353372, 0.21893234198017247, 0.29181917342653707, 0.02128689624073325, 0.0755221785129958, 0.040847216576102435, 0.11711666950889889, 0.001603496496043763, 0.011166218108169191, 0.10154598522683399, 0.20804564927908714, 0.04152706126882266, 0.39221011498043484, 0.0938490411451324, 0.3597112755099759, 0.008686029804384135, 0.24587469948287544, 0.01758912404404562, 0.16700273817492314, 0.005523524512212553, 0.11500859863194643, 0.0238589269426556, 0.31539539811731915, 0.04029533454477179, 0.2232226502248206, 0.06787840431144707, 0.2883958599187324, 0.13953560718108263, 0.2673663502727756, 0.008066585704166612, 0.40576539529889155, 0.00012344681228740494, 0.18632072767535954, 0.9383291479118497, 0.002728743247921069, 0.4930342901234747, 0.15717769986719343, 0.6748972098116224, 0.0021009666448275587, 0.7736883369367374, 0.06816580881374641, 0.9596195731350372, 0.010830958603609348, 0.4892484845932904, 0.21893234198017247, 0.903190925246271, 0.02128689624073325, 0.8420361139149987, 0.040847216576102435, 0.987230285395787, 0.001603496496043763, 0.6904083654940789, 0.10154598522683399, 0.5662628237507425, 0.04152706126882266, 0.5464396833448917, 0.0938490411451324, 0.7454392707127404, 0.008686029804384135, 0.8154081377810312, 0.01758912404404562, 0.8794678768558409, 0.005523524512212553, 0.6607456749400253, 0.0238589269426556, 0.7364820152304077, 0.04029533454477179, 0.6437257357698205, 0.06787840431144707, 0.5930980425461418, 0.13953560718108263, 0.5861680189969418, 0.008066585704166612, 0.8135558255123531, 0.00012344681228740494, 0.058942108840229206, 0.9383291479118497, 0.34978801000933185, 0.4930342901234747, 0.32300182354355017, 0.6748972098116224, 0.15814585424951613, 0.7736883369367374, 0.029549468261353372, 0.9596195731350372, 0.29181917342653707, 0.4892484845932904, 0.0755221785129958, 0.903190925246271, 0.11711666950889889, 0.8420361139149987, 0.011166218108169191, 0.987230285395787, 0.20804564927908714, 0.6904083654940789, 0.39221011498043484, 0.5662628237507425, 0.3597112755099759, 0.5464396833448917, 0.24587469948287544, 0.7454392707127404, 0.16700273817492314, 0.8154081377810312, 0.11500859863194643, 0.8794678768558409, 0.31539539811731915, 0.6607456749400253, 0.2232226502248206, 0.7364820152304077, 0.2883958599187324, 0.6437257357698205, 0.2673663502727756, 0.5930980425461418, 0.40576539529889155, 0.5861680189969418, 0.18632072767535954, 0.8135558255123531, 0.058942108840229206, 0.002728743247921069, 0.34978801000933185, 0.15717769986719343, 0.32300182354355017, 0.0021009666448275587, 0.15814585424951613, 0.06816580881374641, 0.029549468261353372, 0.010830958603609348, 0.29181917342653707, 0.21893234198017247, 0.0755221785129958, 0.02128689624073325, 0.11711666950889889, 0.040847216576102435, 0.011166218108169191, 0.001603496496043763, 0.20804564927908714, 0.10154598522683399, 0.39221011498043484, 0.04152706126882266, 0.3597112755099759, 0.0938490411451324, 0.24587469948287544, 0.008686029804384135, 0.16700273817492314, 0.01758912404404562, 0.11500859863194643, 0.005523524512212553, 0.31539539811731915, 0.0238589269426556, 0.2232226502248206, 0.04029533454477179, 0.2883958599187324, 0.06787840431144707, 0.2673663502727756, 0.13953560718108263, 0.40576539529889155, 0.008066585704166612, 0.18632072767535954, 0.00012344681228740494, 0.9383291479118497, 0.058942108840229206, 0.4930342901234747, 0.34978801000933185, 0.6748972098116224, 0.32300182354355017, 0.7736883369367374, 0.15814585424951613, 0.9596195731350372, 0.029549468261353372, 0.4892484845932904, 0.29181917342653707, 0.903190925246271, 0.0755221785129958, 0.8420361139149987, 0.11711666950889889, 0.987230285395787, 0.011166218108169191, 0.6904083654940789, 0.20804564927908714, 0.5662628237507425, 0.39221011498043484, 0.5464396833448917, 0.3597112755099759, 0.7454392707127404, 0.24587469948287544, 0.8154081377810312, 0.16700273817492314, 0.8794678768558409, 0.11500859863194643, 0.6607456749400253, 0.31539539811731915, 0.7364820152304077, 0.2232226502248206, 0.6437257357698205, 0.2883958599187324, 0.5930980425461418, 0.2673663502727756, 0.5861680189969418, 0.40576539529889155, 0.8135558255123531, 0.18632072767535954, 0.002728743247921069, 0.9383291479118497, 0.15717769986719343, 0.4930342901234747, 0.0021009666448275587, 0.6748972098116224, 0.06816580881374641, 0.7736883369367374, 0.010830958603609348, 0.9596195731350372, 0.21893234198017247, 0.4892484845932904, 0.02128689624073325, 0.903190925246271, 0.040847216576102435, 0.8420361139149987, 0.001603496496043763, 0.987230285395787, 0.10154598522683399, 0.6904083654940789, 0.04152706126882266, 0.5662628237507425, 0.0938490411451324, 0.5464396833448917, 0.008686029804384135, 0.7454392707127404, 0.01758912404404562, 0.8154081377810312, 0.005523524512212553, 0.8794678768558409, 0.0238589269426556, 0.6607456749400253, 0.04029533454477179, 0.7364820152304077, 0.06787840431144707, 0.6437257357698205, 0.13953560718108263, 0.5930980425461418, 0.008066585704166612, 0.5861680189969418, 0.00012344681228740494, 0.8135558255123531}; std::vector w = { 0.0007582310515784511, 0.005585550147583929, 0.0014302457530784435, 0.006269601721311614, 0.005285708929113789, 0.0030672005823594553, 0.008155119403871603, 0.004086439220613566, 0.00515655831762925, 0.002804423566415652, 0.007956607642044452, 0.0007582310515784511, 0.005585550147583929, 0.0014302457530784435, 0.006269601721311614, 0.005285708929113789, 0.0030672005823594553, 0.008155119403871603, 0.004086439220613566, 0.00515655831762925, 0.002804423566415652, 0.007956607642044452, 0.0007582310515784511, 0.005585550147583929, 0.0014302457530784435, 0.006269601721311614, 0.005285708929113789, 0.0030672005823594553, 0.008155119403871603, 0.004086439220613566, 0.00515655831762925, 0.002804423566415652, 0.007956607642044452, 0.00038462648573812436, 0.005226107348461218, 0.0006764119746262043, 0.0032195683537669033, 0.0006360972185858155, 0.006772623286003788, 0.001380765374795222, 0.002256343422743665, 0.0001347461159293506, 0.004693165338141832, 0.00391145922915324, 0.005315198181598307, 0.0015256194116725953, 0.001912683335865006, 0.0008708476156508746, 0.002834576740832729, 0.003290998881926103, 0.004589462082463799, 0.006259183249417213, 0.0018206702056403684, 0.0003443363125208805, 0.00038462648573812436, 0.005226107348461218, 0.0006764119746262043, 0.0032195683537669033, 0.0006360972185858155, 0.006772623286003788, 0.001380765374795222, 0.002256343422743665, 0.0001347461159293506, 0.004693165338141832, 0.00391145922915324, 0.005315198181598307, 0.0015256194116725953, 0.001912683335865006, 0.0008708476156508746, 0.002834576740832729, 0.003290998881926103, 0.004589462082463799, 0.006259183249417213, 0.0018206702056403684, 0.0003443363125208805, 0.00038462648573812436, 0.005226107348461218, 0.0006764119746262043, 0.0032195683537669033, 0.0006360972185858155, 0.006772623286003788, 0.001380765374795222, 0.002256343422743665, 0.0001347461159293506, 0.004693165338141832, 0.00391145922915324, 0.005315198181598307, 0.0015256194116725953, 0.001912683335865006, 0.0008708476156508746, 0.002834576740832729, 0.003290998881926103, 0.004589462082463799, 0.006259183249417213, 0.0018206702056403684, 0.0003443363125208805, 0.00038462648573812436, 0.005226107348461218, 0.0006764119746262043, 0.0032195683537669033, 0.0006360972185858155, 0.006772623286003788, 0.001380765374795222, 0.002256343422743665, 0.0001347461159293506, 0.004693165338141832, 0.00391145922915324, 0.005315198181598307, 0.0015256194116725953, 0.001912683335865006, 0.0008708476156508746, 0.002834576740832729, 0.003290998881926103, 0.004589462082463799, 0.006259183249417213, 0.0018206702056403684, 0.0003443363125208805, 0.00038462648573812436, 0.005226107348461218, 0.0006764119746262043, 0.0032195683537669033, 0.0006360972185858155, 0.006772623286003788, 0.001380765374795222, 0.002256343422743665, 0.0001347461159293506, 0.004693165338141832, 0.00391145922915324, 0.005315198181598307, 0.0015256194116725953, 0.001912683335865006, 0.0008708476156508746, 0.002834576740832729, 0.003290998881926103, 0.004589462082463799, 0.006259183249417213, 0.0018206702056403684, 0.0003443363125208805, 0.00038462648573812436, 0.005226107348461218, 0.0006764119746262043, 0.0032195683537669033, 0.0006360972185858155, 0.006772623286003788, 0.001380765374795222, 0.002256343422743665, 0.0001347461159293506, 0.004693165338141832, 0.00391145922915324, 0.005315198181598307, 0.0015256194116725953, 0.001912683335865006, 0.0008708476156508746, 0.002834576740832729, 0.003290998881926103, 0.004589462082463799, 0.006259183249417213, 0.0018206702056403684, 0.0003443363125208805}; return {std::move(x), std::move(w)}; } else if (m == 30) { // Xiao Gimbutas, 3 points, degree 30 std::vector x = {0.003318724936644646, 0.003318724936644646, 0.07237240722467797, 0.07237240722467797, 0.047157910242171974, 0.047157910242171974, 0.4680301736511254, 0.4680301736511254, 0.01268660467446775, 0.01268660467446775, 0.12159150822272807, 0.12159150822272807, 0.18240956151745308, 0.18240956151745308, 0.36228727935294985, 0.36228727935294985, 0.4367432485484602, 0.4367432485484602, 0.2724280407839283, 0.2724280407839283, 0.49731933900030856, 0.49731933900030856, 0.003318724936644646, 0.9933625501267107, 0.07237240722467797, 0.8552551855506441, 0.047157910242171974, 0.905684179515656, 0.4680301736511254, 0.06393965269774915, 0.01268660467446775, 0.9746267906510645, 0.12159150822272807, 0.7568169835545439, 0.18240956151745308, 0.6351808769650938, 0.36228727935294985, 0.2754254412941003, 0.4367432485484602, 0.1265135029030796, 0.2724280407839283, 0.4551439184321434, 0.49731933900030856, 0.005361321999382884, 0.9933625501267107, 0.003318724936644646, 0.8552551855506441, 0.07237240722467797, 0.905684179515656, 0.047157910242171974, 0.06393965269774915, 0.4680301736511254, 0.9746267906510645, 0.01268660467446775, 0.7568169835545439, 0.12159150822272807, 0.6351808769650938, 0.18240956151745308, 0.2754254412941003, 0.36228727935294985, 0.1265135029030796, 0.4367432485484602, 0.4551439184321434, 0.2724280407839283, 0.005361321999382884, 0.49731933900030856, 0.047835123140772554, 0.25905388452106737, 0.07965952693160062, 0.39157218829125634, 0.05769340127387423, 0.35614028329623354, 0.0772614375768841, 0.2830124973495888, 0.022758384295000066, 0.2416137624515244, 0.1238119787706746, 0.25278124718793293, 0.11588196723610056, 0.18490610638391713, 0.0665174447818816, 0.19370437715364014, 0.00443878137706136, 0.07640124843938755, 0.004663579392688625, 0.20904808745268963, 0.004703681764477044, 0.2985429940592427, 0.025182066703868706, 0.33437904003403107, 0.06577657382474286, 0.12323080238069513, 0.12612409498498942, 0.3385141624298457, 0.19487095092351842, 0.35440360218068745, 0.19100142457228309, 0.2630682977578083, 0.027533406124549888, 0.4345661739646696, 0.028063921981372968, 0.16407098706987833, 0.015902416268934703, 0.0426819997060804, 0.027294230652095765, 0.09395979465272987, 0.005691211445416102, 0.13540885351993445, 0.005162347016621321, 0.3962215147396591, 0.000533708660694491, 0.02948404259767394, 0.6931109923381601, 0.047835123140772554, 0.5287682847771431, 0.07965952693160062, 0.5861663154298922, 0.05769340127387423, 0.6397260650735271, 0.0772614375768841, 0.7356278532534755, 0.022758384295000066, 0.6234067740413924, 0.1238119787706746, 0.6992119263799823, 0.11588196723610056, 0.7397781780644783, 0.0665174447818816, 0.9191599701835511, 0.00443878137706136, 0.7862883331546218, 0.004663579392688625, 0.6967533241762803, 0.004703681764477044, 0.6404388932621002, 0.025182066703868706, 0.8109926237945619, 0.06577657382474286, 0.5353617425851649, 0.12612409498498942, 0.4507254468957941, 0.19487095092351842, 0.5459302776699086, 0.19100142457228309, 0.5379004199107804, 0.027533406124549888, 0.8078650909487487, 0.028063921981372968, 0.9414155840249848, 0.015902416268934703, 0.8787459746951743, 0.027294230652095765, 0.8588999350346495, 0.005691211445416102, 0.5986161382437196, 0.005162347016621321, 0.9699822487416316, 0.000533708660694491, 0.25905388452106737, 0.6931109923381601, 0.39157218829125634, 0.5287682847771431, 0.35614028329623354, 0.5861663154298922, 0.2830124973495888, 0.6397260650735271, 0.2416137624515244, 0.7356278532534755, 0.25278124718793293, 0.6234067740413924, 0.18490610638391713, 0.6992119263799823, 0.19370437715364014, 0.7397781780644783, 0.07640124843938755, 0.9191599701835511, 0.20904808745268963, 0.7862883331546218, 0.2985429940592427, 0.6967533241762803, 0.33437904003403107, 0.6404388932621002, 0.12323080238069513, 0.8109926237945619, 0.3385141624298457, 0.5353617425851649, 0.35440360218068745, 0.4507254468957941, 0.2630682977578083, 0.5459302776699086, 0.4345661739646696, 0.5379004199107804, 0.16407098706987833, 0.8078650909487487, 0.0426819997060804, 0.9414155840249848, 0.09395979465272987, 0.8787459746951743, 0.13540885351993445, 0.8588999350346495, 0.3962215147396591, 0.5986161382437196, 0.02948404259767394, 0.9699822487416316, 0.25905388452106737, 0.047835123140772554, 0.39157218829125634, 0.07965952693160062, 0.35614028329623354, 0.05769340127387423, 0.2830124973495888, 0.0772614375768841, 0.2416137624515244, 0.022758384295000066, 0.25278124718793293, 0.1238119787706746, 0.18490610638391713, 0.11588196723610056, 0.19370437715364014, 0.0665174447818816, 0.07640124843938755, 0.00443878137706136, 0.20904808745268963, 0.004663579392688625, 0.2985429940592427, 0.004703681764477044, 0.33437904003403107, 0.025182066703868706, 0.12323080238069513, 0.06577657382474286, 0.3385141624298457, 0.12612409498498942, 0.35440360218068745, 0.19487095092351842, 0.2630682977578083, 0.19100142457228309, 0.4345661739646696, 0.027533406124549888, 0.16407098706987833, 0.028063921981372968, 0.0426819997060804, 0.015902416268934703, 0.09395979465272987, 0.027294230652095765, 0.13540885351993445, 0.005691211445416102, 0.3962215147396591, 0.005162347016621321, 0.02948404259767394, 0.000533708660694491, 0.6931109923381601, 0.25905388452106737, 0.5287682847771431, 0.39157218829125634, 0.5861663154298922, 0.35614028329623354, 0.6397260650735271, 0.2830124973495888, 0.7356278532534755, 0.2416137624515244, 0.6234067740413924, 0.25278124718793293, 0.6992119263799823, 0.18490610638391713, 0.7397781780644783, 0.19370437715364014, 0.9191599701835511, 0.07640124843938755, 0.7862883331546218, 0.20904808745268963, 0.6967533241762803, 0.2985429940592427, 0.6404388932621002, 0.33437904003403107, 0.8109926237945619, 0.12323080238069513, 0.5353617425851649, 0.3385141624298457, 0.4507254468957941, 0.35440360218068745, 0.5459302776699086, 0.2630682977578083, 0.5379004199107804, 0.4345661739646696, 0.8078650909487487, 0.16407098706987833, 0.9414155840249848, 0.0426819997060804, 0.8787459746951743, 0.09395979465272987, 0.8588999350346495, 0.13540885351993445, 0.5986161382437196, 0.3962215147396591, 0.9699822487416316, 0.02948404259767394, 0.047835123140772554, 0.6931109923381601, 0.07965952693160062, 0.5287682847771431, 0.05769340127387423, 0.5861663154298922, 0.0772614375768841, 0.6397260650735271, 0.022758384295000066, 0.7356278532534755, 0.1238119787706746, 0.6234067740413924, 0.11588196723610056, 0.6992119263799823, 0.0665174447818816, 0.7397781780644783, 0.00443878137706136, 0.9191599701835511, 0.004663579392688625, 0.7862883331546218, 0.004703681764477044, 0.6967533241762803, 0.025182066703868706, 0.6404388932621002, 0.06577657382474286, 0.8109926237945619, 0.12612409498498942, 0.5353617425851649, 0.19487095092351842, 0.4507254468957941, 0.19100142457228309, 0.5459302776699086, 0.027533406124549888, 0.5379004199107804, 0.028063921981372968, 0.8078650909487487, 0.015902416268934703, 0.9414155840249848, 0.027294230652095765, 0.8787459746951743, 0.005691211445416102, 0.8588999350346495, 0.005162347016621321, 0.5986161382437196, 0.000533708660694491, 0.9699822487416316}; std::vector w = {8.586495068552472e-05, 0.001900966334207759, 0.0014222168338437478, 0.0038920643661654237, 0.0004232750726644445, 0.0036934559174571445, 0.005376425482716406, 0.007798045662912898, 0.006202479514073004, 0.00745727538134288, 0.0013956590598703843, 8.586495068552472e-05, 0.001900966334207759, 0.0014222168338437478, 0.0038920643661654237, 0.0004232750726644445, 0.0036934559174571445, 0.005376425482716406, 0.007798045662912898, 0.006202479514073004, 0.00745727538134288, 0.0013956590598703843, 8.586495068552472e-05, 0.001900966334207759, 0.0014222168338437478, 0.0038920643661654237, 0.0004232750726644445, 0.0036934559174571445, 0.005376425482716406, 0.007798045662912898, 0.006202479514073004, 0.00745727538134288, 0.0013956590598703843, 0.002107379231956217, 0.0029624613725460075, 0.0029721100922122435, 0.003438592193767963, 0.0020462494841735996, 0.0044363272530086725, 0.003767161464773241, 0.003402003378050937, 0.000603348284271059, 0.0009794742889466218, 0.0011372229504993392, 0.002682342423093155, 0.0029350349015327634, 0.005651511771468744, 0.007158242634833795, 0.006463011725059148, 0.003140798290445832, 0.0023509684970529836, 0.0009152534380744139, 0.0019109402735475102, 0.0009599402787344589, 0.0013212839615816257, 0.00016781085573320113, 0.002107379231956217, 0.0029624613725460075, 0.0029721100922122435, 0.003438592193767963, 0.0020462494841735996, 0.0044363272530086725, 0.003767161464773241, 0.003402003378050937, 0.000603348284271059, 0.0009794742889466218, 0.0011372229504993392, 0.002682342423093155, 0.0029350349015327634, 0.005651511771468744, 0.007158242634833795, 0.006463011725059148, 0.003140798290445832, 0.0023509684970529836, 0.0009152534380744139, 0.0019109402735475102, 0.0009599402787344589, 0.0013212839615816257, 0.00016781085573320113, 0.002107379231956217, 0.0029624613725460075, 0.0029721100922122435, 0.003438592193767963, 0.0020462494841735996, 0.0044363272530086725, 0.003767161464773241, 0.003402003378050937, 0.000603348284271059, 0.0009794742889466218, 0.0011372229504993392, 0.002682342423093155, 0.0029350349015327634, 0.005651511771468744, 0.007158242634833795, 0.006463011725059148, 0.003140798290445832, 0.0023509684970529836, 0.0009152534380744139, 0.0019109402735475102, 0.0009599402787344589, 0.0013212839615816257, 0.00016781085573320113, 0.002107379231956217, 0.0029624613725460075, 0.0029721100922122435, 0.003438592193767963, 0.0020462494841735996, 0.0044363272530086725, 0.003767161464773241, 0.003402003378050937, 0.000603348284271059, 0.0009794742889466218, 0.0011372229504993392, 0.002682342423093155, 0.0029350349015327634, 0.005651511771468744, 0.007158242634833795, 0.006463011725059148, 0.003140798290445832, 0.0023509684970529836, 0.0009152534380744139, 0.0019109402735475102, 0.0009599402787344589, 0.0013212839615816257, 0.00016781085573320113, 0.002107379231956217, 0.0029624613725460075, 0.0029721100922122435, 0.003438592193767963, 0.0020462494841735996, 0.0044363272530086725, 0.003767161464773241, 0.003402003378050937, 0.000603348284271059, 0.0009794742889466218, 0.0011372229504993392, 0.002682342423093155, 0.0029350349015327634, 0.005651511771468744, 0.007158242634833795, 0.006463011725059148, 0.003140798290445832, 0.0023509684970529836, 0.0009152534380744139, 0.0019109402735475102, 0.0009599402787344589, 0.0013212839615816257, 0.00016781085573320113, 0.002107379231956217, 0.0029624613725460075, 0.0029721100922122435, 0.003438592193767963, 0.0020462494841735996, 0.0044363272530086725, 0.003767161464773241, 0.003402003378050937, 0.000603348284271059, 0.0009794742889466218, 0.0011372229504993392, 0.002682342423093155, 0.0029350349015327634, 0.005651511771468744, 0.007158242634833795, 0.006463011725059148, 0.003140798290445832, 0.0023509684970529836, 0.0009152534380744139, 0.0019109402735475102, 0.0009599402787344589, 0.0013212839615816257, 0.00016781085573320113}; return {std::move(x), std::move(w)}; } else throw std::runtime_error("Xiao-Gimbutas not implemented for this order."); } else if (celltype == cell::type::tetrahedron) { if (m == 1) { // Xiao Gimbutas, 4 points, degree 1 std::vector x = {0.25, 0.25, 0.25}; std::vector w = {0.16666666666666666}; return {std::move(x), std::move(w)}; } else if (m == 2) { // Xiao Gimbutas, 4 points, degree 2 std::vector x = {0.1236668003284584, 0.8215725409676198, 0.03993304864149842, 0.4574615870855955, 0.155933120499186, 0.3817653560693467, 0.3653145188146345, 0.1800296935103654, 0.006923235573627467, 0.0003755150287292757, 0.2160764291848478, 0.4307017070778361}; std::vector w = {0.016934591412496782, 0.04646292944776137, 0.050086823222829334, 0.05318232258357918}; return {std::move(x), std::move(w)}; } else if (m == 3) { // Xiao Gimbutas, 4 points, degree 3 std::vector x = {0.6414297914956963, 0.1620014916985245, 0.1838503504920977, 0.3454441557197307, 0.01090521221118924, 0.2815238021235462, 0.439858947649275, 0.1901170024392839, 0.01140332944455717, 0.03787163178235702, 0.170816925164989, 0.1528181430909273, 0.1248048621652472, 0.1586851632274406, 0.5856628056552158, 0.1414827519695045, 0.5712260521491151, 0.1469183900871696}; std::vector w = {0.020387000459557516, 0.021344402118457815, 0.022094671190740867, 0.0234374016100672, 0.0374025278195929, 0.042000663468250383}; return {std::move(x), std::move(w)}; } else if (m == 4) { // Xiao Gimbutas, 4 points, degree 4 std::vector x = {0.1746940586972306, 0.04049050672759043, 0.01356070187980288, 0.08140491840285925, 0.752508507009655, 0.06809937093820666, 0.7412288820936226, 0.0672232948933834, 0.03518392977359872, 0.05334123953574518, 0.419266313879513, 0.04778143555908666, 0.4329534904813556, 0.4507658760912768, 0.05945661629943383, 0.5380072039161857, 0.1294113737889104, 0.3301904148374645, 0.00899126009333578, 0.1215419913339278, 0.3064939884296903, 0.1066041725619936, 0.09720464458758327, 0.68439041545304, 0.3292329597426469, 0.02956949520647961, 0.3179035602133946, 0.1038441164109932, 0.4327102390477686, 0.3538232392092971, 0.3044484024344968, 0.2402766649280726, 0.126801725915392}; std::vector w = {0.006541848487473326, 0.009212228192656149, 0.009232299811929395, 0.009988864191093254, 0.011578327656272562, 0.012693785874259726, 0.013237780011337552, 0.01774467235924835, 0.018372372071416284, 0.02582935266937435, 0.03223513534160575}; return {std::move(x), std::move(w)}; } else if (m == 5) { // Xiao Gimbutas, 4 points, degree 5 std::vector x = {0.4544962958743503, 0.4544962958743504, 0.04550370412564962, 0.04550370412564967, 0.4544962958743504, 0.4544962958743504, 0.04550370412564973, 0.4544962958743503, 0.04550370412564969, 0.4544962958743503, 0.04550370412564966, 0.4544962958743504, 0.4544962958743503, 0.04550370412564968, 0.04550370412564962, 0.0455037041256497, 0.04550370412564966, 0.4544962958743504, 0.09273525031089128, 0.7217942490673263, 0.09273525031089122, 0.721794249067326, 0.09273525031089128, 0.09273525031089129, 0.09273525031089132, 0.09273525031089114, 0.09273525031089129, 0.0927352503108913, 0.0927352503108913, 0.7217942490673263, 0.3108859192633006, 0.06734224221009831, 0.3108859192633006, 0.06734224221009824, 0.3108859192633006, 0.3108859192633007, 0.3108859192633006, 0.3108859192633007, 0.3108859192633006, 0.3108859192633006, 0.3108859192633007, 0.06734224221009814}; std::vector w = {0.0070910034628469025, 0.007091003462846909, 0.007091003462846909, 0.007091003462846912, 0.007091003462846912, 0.0070910034628469155, 0.012248840519393652, 0.012248840519393652, 0.012248840519393655, 0.012248840519393659, 0.018781320953002632, 0.018781320953002632, 0.018781320953002632, 0.01878132095300265}; return {std::move(x), std::move(w)}; } else if (m == 6) { // Xiao Gimbutas, 4 points, degree 6 std::vector x = {0.02431897424814286, 0.03883608434488445, 0.9029287990136113, 0.02286582381402311, 0.9037700013321819, 0.02933572108317866, 0.008781957777518898, 0.0405760510668179, 0.08860035046891021, 0.8411389516623184, 0.05132520616520296, 0.0372647521383555, 0.2112976585815863, 0.007354523838069352, 0.2511844952775297, 0.02346779557305456, 0.06477516044710505, 0.3908620506710118, 0.2130411832361856, 0.06001058302026912, 0.02584268626070331, 0.2678441981835756, 0.06476943693005288, 0.6367675085585139, 0.05399614083591447, 0.2757863004698506, 0.06001614916616868, 0.3293797185491983, 0.3251196585770252, 0.3268335046190458, 0.6243213635534294, 0.06592492316000995, 0.2535936747432003, 0.06319998094256953, 0.6174557201472688, 0.2584491489839256, 0.248449540118895, 0.6265402017088824, 0.06211553318359875, 0.06373289529499766, 0.277903669330078, 0.5949096890217955, 0.06517799276337043, 0.5947173018757956, 0.06660329800760315, 0.08367881406005505, 0.06609866241468051, 0.6300545551109896, 0.5773457813897267, 0.2877250948264642, 0.06462063807336853, 0.038288670738245, 0.3283881712312217, 0.3202874336976925, 0.3519391973347045, 0.05509902249072568, 0.3810843089063102, 0.5300632754810166, 0.06678959978173812, 0.07699271710096725, 0.1521038113099309, 0.1246499636374863, 0.201234567364421, 0.3041692653497818, 0.3191942803489312, 0.04438334435720821, 0.2558207842649862, 0.2794200529459882, 0.269569929633272}; std::vector w = { 0.0011826324752765881, 0.001206879481977829, 0.0017372226206159916, 0.0026542465308339587, 0.0037609445463571384, 0.0040385478129073915, 0.00425072071117374, 0.005251568313784406, 0.006619016274847046, 0.0072065494492455666, 0.007265066343438196, 0.007768855687763452, 0.007858005078710203, 0.008148345983740361, 0.008294771681919054, 0.00883888731802823, 0.008989168438051998, 0.009970224610238195, 0.010435745880218545, 0.010511060314253423, 0.010722336995514588, 0.011189302702092839, 0.018766567415678}; return {std::move(x), std::move(w)}; } else if (m == 7) { // Xiao Gimbutas, 4 points, degree 7 std::vector x = {0.001996825818299818, 0.01920799348858535, 0.6513348958482376, 0.06092218458545083, 0.3234568417895977, 0.6151709883118704, 0.0005004334442718418, 0.6355215105837613, 0.0598944722319085, 0.6279832293585974, 0.293770036523707, 0.02748237819283441, 0.05213668905801093, 0.06201109193664409, 0.05718215451677883, 0.8245440666953954, 0.05989419506998693, 0.05677586668994691, 0.062815072845237, 0.8207453007415948, 0.05891041282560915, 0.6315484739180046, 0.02583316773173042, 0.280405238101906, 0.001613532619990097, 0.1991105720528834, 0.2637473753385648, 0.3196583760970118, 0.5991009436200256, 0.03521379529745457, 0.5508889781422127, 0.2234702025301428, 0.2255285376972644, 0.3505284068372833, 0.003929651487087849, 0.2418446130585829, 0.2307849002376704, 0.01403308447330531, 0.5661630745306973, 0.063969430325799, 0.06247402252315021, 0.812872555571, 0.3239480709891824, 0.0624444127129091, 0.5863212858301218, 0.2343964973359623, 0.528711306413653, 0.2169982993458658, 0.3501153045071709, 0.2615087691765827, 0.01197587688915757, 0.2753098106871322, 0.05300013833454678, 0.04497766312688006, 0.0762414702839689, 0.03316569983103569, 0.2748015348979936, 0.02253298349383202, 0.2604205879982621, 0.5646501024676913, 0.04463026790663657, 0.6018235776318118, 0.2899940343655131, 0.5990192398798975, 0.0528776293788545, 0.05497958289551413, 0.0680111048992614, 0.2977852343835241, 0.04764944310089234, 0.1572418559860032, 0.5504559416248597, 0.04374347016073189, 0.5003786698498154, 0.2582805473674438, 0.08266909560739215, 0.2898612819086906, 0.3971744029949173, 0.1710488778187786, 0.1112511334269427, 0.1074624307831534, 0.4758491617393153, 0.07400870213911578, 0.4103580959949396, 0.2447616509193416, 0.2175544442163533, 0.2521305306293954, 0.4487392553835752, 0.4372480897645487, 0.1098959763270211, 0.2765716827388576, 0.2188126225475045, 0.176438223014948, 0.1757661102664513}; std::vector w = { 0.0012846968603334146, 0.002000632031369977, 0.002085684575720105, 0.002783666843940815, 0.0030095129140263084, 0.0032004686326964665, 0.0034317247720467565, 0.003452013693960475, 0.0034787841936317, 0.0035154609736464167, 0.0036967198625352964, 0.003724778580430695, 0.0037352275658985514, 0.003869468221310365, 0.0038818311595533, 0.00409373831432887, 0.004554985719607738, 0.0046768630523221786, 0.00471879793532209, 0.004799380599205763, 0.005235187909249938, 0.005632644217125163, 0.0061559681852397475, 0.006973088601266729, 0.007165193660663451, 0.008796944275592277, 0.010050252598534952, 0.010698139822576646, 0.011118534527621958, 0.011123248236813444, 0.01372302813009497}; return {std::move(x), std::move(w)}; } else if (m == 8) { // Xiao Gimbutas, 4 points, degree 8 std::vector x = {0.0009718783690150193, 0.9573816260583027, 0.01600798423129822, 0.01760715612687848, 0.008855594278705747, 0.9485639297990048, 0.9408082028316022, 0.0300522477759322, 0.006719678822153188, 0.004571184374515704, 0.04868900463902199, 0.0221620710966624, 0.4618117251682627, 0.4653151604130365, 0.005422183307178087, 0.03576257087872006, 0.5003296720329762, 0.008112050755706058, 0.2307434967946494, 0.6113365336918841, 0.005575773030438238, 0.02580684887253231, 0.02777675799356541, 0.4665405858909191, 0.4139212834388201, 0.4982549617822043, 0.07975787096690573, 0.1999067290329215, 0.4794066533528659, 0.3200956067503981, 0.1643869309848081, 0.03985667792342303, 0.0344820754789425, 0.03678064973474819, 0.7205111657178718, 0.2043689239073254, 0.1890744688700332, 0.03620467192851837, 0.7313673600360894, 0.1506377117814253, 0.7623678849544528, 0.05243838992870327, 0.2496370830578753, 0.1955460819226021, 0.5417931824618095, 0.2579166731543092, 0.2209774649988177, 0.01239592336019548, 0.05045500693172469, 0.03938021868450564, 0.1731956086833511, 0.7485745607457628, 0.04543453035210961, 0.1632993432993541, 0.04648612941109276, 0.1564866850400663, 0.7514965361518376, 0.723709495041749, 0.04620058214866413, 0.04349881445195029, 0.04908534432749626, 0.04149415901827835, 0.71717735709395, 0.7016567760643901, 0.2044918137472231, 0.0468686532808731, 0.04478021549603177, 0.719573330071695, 0.05599064468210205, 0.4487780077524824, 0.04105029814548231, 0.04568536574611817, 0.04312238337787981, 0.4150765842699887, 0.4947991693199913, 0.03095052921613287, 0.4246500540795894, 0.1362062412251983, 0.2504719219173209, 0.02261090963666046, 0.2224346424927786, 0.05438301560623344, 0.2162641488906512, 0.05125376513387382, 0.4721332195496732, 0.04544737426285883, 0.4356669945456628, 0.03082247209993105, 0.1812621911709453, 0.2593463891596102, 0.5111060719135769, 0.02882725890093539, 0.2295407130726556, 0.1239784893278184, 0.06224439946102048, 0.4036547750100588, 0.3828805011657271, 0.397275457461443, 0.08173362820479114, 0.4935900808340203, 0.2161396926300791, 0.03919057319753563, 0.03383673809722395, 0.2125192261175291, 0.5056419509803703, 0.04243030114562282, 0.4862252458661596, 0.2752360272999065, 0.2502274462930384, 0.04529074988858361, 0.4931447679590647, 0.2280801840550788, 0.1425146299088945, 0.1281437397666177, 0.4835182802910101, 0.2295406150635747, 0.2420175573174804, 0.1822460492233608, 0.4284077707360187, 0.06194696154684466, 0.2096423028706461, 0.4853135576380692, 0.1986199776692312, 0.1646955059003284, 0.2495207660799524, 0.249905550725755, 0.4118962036484124, 0.1582590933458902, 0.2073917351406235, 0.2003307068778031, 0.2274620499867547, 0.4489545286636978}; std::vector w = { 0.0002523242325191773, 0.000290863922550942, 0.00034651409450314665, 0.0004244834198904958, 0.0011495713860626099, 0.0012535250586434693, 0.0016460861324811202, 0.0016881535417914368, 0.00197757011617887, 0.0021332305621032666, 0.0022892954263809117, 0.002453317862571215, 0.00251062824751718, 0.002564740902068485, 0.002602731533963437, 0.0027548944667587136, 0.0028292048763357883, 0.0028982004270852453, 0.002935422697522245, 0.002983133967882152, 0.0029981483163099916, 0.0033577851411315802, 0.0034241727240937898, 0.0036540831010869316, 0.0037507609946701936, 0.0037598731255837213, 0.0037906201057506216, 0.003916487256448844, 0.00402639065463561, 0.00418984767843144, 0.004519589747580942, 0.004542489322412185, 0.004697614316667847, 0.0053735422239879, 0.005382166773474393, 0.0054545457922569145, 0.006091681784605513, 0.006510451658158449, 0.006683134190221676, 0.006927915069245575, 0.008629937323168083, 0.008810488117670844, 0.008892300407975902, 0.009298747966287926}; return {std::move(x), std::move(w)}; } else if (m == 9) { // Xiao Gimbutas, 4 points, degree 9 std::vector x = {0.006891366839295159, 0.7203807203359198, 0.2440786635406753, 0.1555789083027259, 0.7830238380676567, 0.03722731406074435, 0.8847735952623145, 0.03746715582104113, 0.04313984945003826, 0.04038577127605769, 0.2233476678136009, 0.723724094807067, 0.515285061557277, 0.4405214048473231, 0.03307435692542832, 0.03474022441842899, 0.8739842844692827, 0.04802973388725062, 0.2737549691522871, 0.5866194063678511, 0.1331737132533926, 0.03138783669262657, 0.02302476689782933, 0.2010151099960878, 0.04195473200768755, 0.04206648540987851, 0.8751960265284056, 0.2133537804622216, 0.01492382397847686, 0.72761122983064, 0.05137368203860852, 0.737898449375068, 0.01052487006572961, 0.01167918776735336, 0.03967712051591504, 0.47317164143189, 0.6123408021820864, 0.1432143403978234, 0.0006662179087437572, 0.04585901947602267, 0.04589832979249515, 0.03930065125704704, 0.1030115138063945, 0.2313142488715318, 0.002864577988396536, 0.00837326895113094, 0.218303550548395, 0.0638376081133778, 0.7518893444883036, 0.03020971497018418, 0.03931328269724702, 0.2307731120717099, 0.03171156153538288, 0.02914913102984441, 0.005807381258551988, 0.4350661767994387, 0.4471841503422259, 0.3807370349415273, 0.1946216802654709, 0.4205051742028665, 0.2760774398234713, 0.6335347557774519, 0.02029485866160777, 0.06107660031735783, 0.4443452478104636, 0.474856882867235, 0.04011137824044635, 0.03313166695541811, 0.713691124061697, 0.02545934399020729, 0.1809521324516702, 0.6820586129684939, 0.6044262999606023, 0.01758681325508173, 0.2037168634602605, 0.7098027195586388, 0.04483105915111014, 0.211317992359171, 0.7304989942620459, 0.1871472921145495, 0.03706986589769196, 0.5007701133228333, 0.02448223828136365, 0.04933213970351709, 0.03332720320375657, 0.4837323966974713, 0.0372285314523825, 0.3567521368987188, 0.1328834330395908, 0.02051457140178872, 0.1838549978176258, 0.02094427630108402, 0.1704389178072653, 0.2268373199643008, 0.4167820833252522, 0.007798392818596213, 0.1923717952701477, 0.1242856487305183, 0.6561382235043771, 0.3619508813743887, 0.01552011599218286, 0.3924336805270703, 0.01965056042084037, 0.6443583098389682, 0.1340945354491865, 0.458244669513536, 0.0366754648901354, 0.4635857264004116, 0.01538959981066759, 0.3394322940952808, 0.2418695119980843, 0.1242356229882021, 0.02743271521385679, 0.4125431242926855, 0.5655163520825289, 0.2158455335267193, 0.1861909388269384, 0.1031571468282741, 0.6454494634370023, 0.2025517153954042, 0.04580873781449243, 0.1306656875755306, 0.2354771046476909, 0.2222475737162643, 0.3744969790769868, 0.3639253527626089, 0.4587456757955503, 0.360156713211527, 0.03689983110212465, 0.3633566801123643, 0.4147619222990371, 0.1625263637506013, 0.1839853909173693, 0.05281258992885371, 0.5791440724832208, 0.03912033521689179, 0.1799045015970503, 0.4765094010040439, 0.1524683171735235, 0.1476870681014711, 0.09407076219108002, 0.1668749728706788, 0.5729563685802239, 0.07774426835124648, 0.1417161598745218, 0.2271598693150327, 0.5175989795285014, 0.3516091771883741, 0.05503739382968048, 0.1969410619376471, 0.3364345271215077, 0.2289840090706933, 0.0827335053473291, 0.10961245832859, 0.3537003584345384, 0.1179344061919236, 0.5571603219749478, 0.1325043854190557, 0.1224035951881698, 0.08342378852063631, 0.4222286016719213, 0.3031418645744979, 0.2709662131081065, 0.3336191905680228, 0.1998230301014543, 0.3787443941211687, 0.143482922419707, 0.3469408062547597, 0.1819772234774806, 0.1650628409073057, 0.3092464405790883}; std::vector w = { 0.0007162016044045418, 0.0009077771144629987, 0.0009168420416400242, 0.0009750266138313078, 0.0011001900925731798, 0.0011235667369375677, 0.001171941882444346, 0.0011744621157262513, 0.0011822813558184364, 0.001184296682706247, 0.0011900066193666035, 0.0011999643701743605, 0.001313356829704151, 0.0013368684225426673, 0.0013663833806215103, 0.0013827059604750302, 0.0015244284474830849, 0.001527532399215677, 0.0016299004290249847, 0.0016375601841337004, 0.0017185809772670582, 0.00193749218890168, 0.0019604227565410766, 0.0020954845204123134, 0.0021625430372239733, 0.0021645885229682614, 0.00218097825718004, 0.002205183817607957, 0.0022123193411248848, 0.0022143750651612668, 0.0022795392347588314, 0.0023777410142237997, 0.002482591391785385, 0.00251979649860075, 0.0025639554658188667, 0.0026195628789069518, 0.00274589846301462, 0.00331480176776181, 0.0035590499516530514, 0.00373011850287772, 0.0038349148702950617, 0.004215437802807641, 0.0042320852343912235, 0.004275313663630222, 0.0043800821139580864, 0.00439593140299428, 0.00468786977428942, 0.005307550768351452, 0.005439951471336866, 0.0055201790703796666, 0.005870060438901905, 0.005879894123846905, 0.005952755449705822, 0.006370180985658904, 0.006979224618881529, 0.007535230513202575, 0.008183687426958101}; return {std::move(x), std::move(w)}; } else if (m == 10) { // Xiao Gimbutas, 4 points, degree 10 std::vector x = {0.004844889527768171, 0.004844889527573282, 0.9854653314167374, 0.946719848292354, 0.006667080448351054, 0.00806750556190488, 0.006667080448658992, 0.03854556569737443, 0.008067505561779886, 0.0385455656974801, 0.9467198482918522, 0.008067505562173938, 0.00382689193302969, 0.8740251327651677, 0.01855431505435714, 0.1035936602480028, 0.003826891933043923, 0.01855431505433685, 0.8740251327649323, 0.1035936602475548, 0.01855431505434555, 0.01735404744568466, 0.6202583012900509, 0.0324034637009163, 0.6202583012898868, 0.3299841875634282, 0.03240346370091293, 0.3299841875637114, 0.01735404744578351, 0.03240346370092632, 0.03118131875220204, 0.1266394908785819, 0.8114772294984899, 0.1266394908788713, 0.03070196087069638, 0.8114772294981942, 0.03070196087074504, 0.03118131875221086, 0.8114772294979709, 0.7805722458243883, 0.03518958226876812, 0.03307359982554328, 0.03518958226870847, 0.1511645720816399, 0.03307359982557704, 0.1511645720815177, 0.7805722458241509, 0.03307359982557292, 0.8149022829407979, 0.03351563550542005, 0.1193767951360695, 0.03351563550541749, 0.03220528641772838, 0.1193767951358615, 0.03220528641771444, 0.8149022829405032, 0.1193767951363346, 0.5596600888554596, 0.1807602495702272, 0.007438795731167321, 0.1807602495700562, 0.2521408658433209, 0.007438795731199615, 0.2521408658430335, 0.5596600888557026, 0.007438795731214367, 0.03029393775770431, 0.3510309266619137, 0.03266058005326467, 0.3510309266617719, 0.5860145555272518, 0.03266058005328976, 0.5860145555274775, 0.03029393775772133, 0.03266058005327201, 0.03114461249008678, 0.5992738011392268, 0.3365975263830276, 0.03298405998767925, 0.03114461249007869, 0.3365975263825117, 0.5992738011395161, 0.03298405998767671, 0.3365975263827067, 0.01267643164854561, 0.1645472158899154, 0.4090752240119478, 0.1645472158900111, 0.4137011284494345, 0.4090752240120063, 0.4137011284495361, 0.01267643164855134, 0.4090752240118714, 0.03573908441154557, 0.03377912559111834, 0.592532870170714, 0.3379489198264275, 0.03573908441154142, 0.5925328701708977, 0.03377912559113959, 0.3379489198260952, 0.5925328701712224, 0.1336599495323923, 0.1336599495326111, 0.5990201514025275, 0.1669597693172063, 0.1669597693175803, 0.4991206920481123, 0.0122804285624344, 0.4126008361791699, 0.3979043621857173, 0.1772143730727422, 0.01228042856244396, 0.3979043621855937, 0.4126008361792021, 0.1772143730727065, 0.3979043621855659, 0.3696126448561478, 0.1145379863160246, 0.02495402206716512, 0.1145379863158415, 0.4908953467609971, 0.02495402206723796, 0.4908953467605529, 0.369612644856217, 0.0249540220672398, 0.08302494688196357, 0.7282094809974271, 0.03849678186899388, 0.7282094809970688, 0.1502687902517552, 0.03849678186899534, 0.1502687902517038, 0.08302494688216776, 0.03849678186896475, 0.01680959146959896, 0.4043770144883662, 0.1613371421710751, 0.417476251870908, 0.01680959146964861, 0.1613371421710812, 0.4043770144883684, 0.4174762518708103, 0.1613371421711606, 0.1696138475604023, 0.6339280343903018, 0.1733608592737587, 0.02309725877555648, 0.1696138475603947, 0.1733608592737318, 0.6339280343903569, 0.0230972587755699, 0.1733608592736602, 0.02434102832026448, 0.6335875653142609, 0.1650530553465075, 0.1770183510189467, 0.02434102832027353, 0.1650530553463749, 0.6335875653143981, 0.1770183510189177, 0.1650530553463994, 0.02935511686037216, 0.1740037654545786, 0.6208415104357218, 0.1740037654546278, 0.1757996072492151, 0.6208415104357764, 0.1757996072493023, 0.02935511686041814, 0.6208415104356415, 0.1121243864649791, 0.5325889236500517, 0.2362419588457611, 0.1190447310392825, 0.112124386465023, 0.2362419588456988, 0.5325889236499357, 0.119044731039276, 0.2362419588456608, 0.1193225934917416, 0.4534859243589937, 0.1285563923599583, 0.2986350897893351, 0.119322593491917, 0.1285563923597633, 0.4534859243587895, 0.298635089789364, 0.1285563923599528, 0.3020019073266451, 0.3283455214738413, 0.3030261289420949, 0.3283455214738281, 0.0666264422574022, 0.3030261289418549, 0.06662644225731254, 0.3020019073267441, 0.3030261289420782, 0.5159168166142745, 0.1324670107447715, 0.1074464615850585, 0.1324670107446421, 0.244169711055904, 0.1074464615851541, 0.2441697110556646, 0.5159168166144414, 0.1074464615851919, 0.318206200820324, 0.3182062008205924, 0.04538139753862875, 0.1207131116358087, 0.3395130626211381, 0.4110926574948671, 0.3395130626209017, 0.1286811682481305, 0.4110926574949583, 0.1286811682481935, 0.1207131116358556, 0.4110926574950111, 0.2570719807624278, 0.2570719807625507, 0.2287840577124626}; std::vector w = {6.365271938318e-05, 0.00013168318579095435, 0.00013168318579197532, 0.00013168318579277535, 0.0002820943065241142, 0.0002820943065245265, 0.0002820943065259753, 0.0010231923733042463, 0.0010231923733075212, 0.0010231923733079599, 0.0011033909837475048, 0.0011033909837480274, 0.0011033909837487538, 0.00116416016749959, 0.0011641601675003968, 0.0011641601675006052, 0.001191461909106609, 0.0011914619091069783, 0.0011914619091077897, 0.001368674554135069, 0.0013686745541369087, 0.0013686745541372192, 0.001522996656033314, 0.00152299665603406, 0.0015229966560349054, 0.0016958310152239383, 0.0016958310152242648, 0.00169583101522512, 0.0018736985584823533, 0.0018736985584836066, 0.0018736985584844332, 0.0018987030975030218, 0.0018987030975037966, 0.0018987030975040383, 0.0019028288945829631, 0.0019591843153447786, 0.001969493864818562, 0.001969493864819973, 0.001969493864825328, 0.002064660777657845, 0.0020646607776616814, 0.0020646607776622417, 0.002083913166714165, 0.002083913166715285, 0.0020839131667166597, 0.002094685984369893, 0.002094685984373115, 0.002094685984375275, 0.00219141969443908, 0.0021914196944394283, 0.002191419694440678, 0.002284450780288395, 0.0022844507802891865, 0.002284450780289795, 0.0027774049091755302, 0.00277740490917618, 0.0027774049091794984, 0.003445098889335028, 0.0034450988893415164, 0.0034450988893420715, 0.004234512641858648, 0.004234512641859507, 0.004234512641860643, 0.004471157472950426, 0.004471157472951478, 0.004471157472952013, 0.0045538831991611866, 0.004553883199161758, 0.004553883199163369, 0.004651927948037353, 0.004682093742486137, 0.004682093742490215, 0.004682093742491071, 0.0077630869974032015}; return {std::move(x), std::move(w)}; } else if (m == 11) { // Xiao Gimbutas, 4 points, degree 11 std::vector x = {0.02174356161974667, 0.02174356162123492, 0.9347693151393625, 0.01214887718924207, 0.4733927262830146, 0.5068373557636653, 0.473392726282396, 0.007621040764793446, 0.5068373557636205, 0.007621040764693851, 0.01214887718929203, 0.5068373557640636, 0.8283411311394426, 0.01186743374839475, 0.03349202862194989, 0.1262994064902889, 0.8283411311393326, 0.03349202862186627, 0.01186743374856977, 0.1262994064902864, 0.03349202862190801, 0.03884286462495556, 0.0266245121640002, 0.02791260212206558, 0.906620021088922, 0.03884286462496065, 0.02791260212209652, 0.02662451216400466, 0.9066200210888388, 0.02791260212224616, 0.01779838979231115, 0.02570671787901103, 0.1452959108855631, 0.8111989814430212, 0.01779838979236296, 0.1452959108856611, 0.02570671787902293, 0.8111989814422269, 0.1452959108864052, 0.4336893678654693, 0.03413212384063684, 0.004124974507952623, 0.5280535337860727, 0.4336893678650325, 0.0041249745082621, 0.03413212384056524, 0.528053533786363, 0.004124974508272195, 0.02723844389731109, 0.02928619896418311, 0.8221371579049448, 0.1213381992336525, 0.02723844389723012, 0.8221371579047962, 0.0292861989645277, 0.1213381992371994, 0.8221371579007392, 0.1055219304212634, 0.4058583387143239, 0.4825471524757858, 0.40585833871604, 0.006072578389145572, 0.4825471524736373, 0.00607257838916173, 0.1055219304213615, 0.482547152473874, 0.7375158260909277, 0.09328386025279926, 0.01187574741300857, 0.09328386025323457, 0.1573245662432324, 0.01187574741303624, 0.1573245662429647, 0.7375158260902062, 0.01187574741367739, 0.7570556764755156, 0.1851613866917053, 0.02607843400608577, 0.1851613866916737, 0.03170450282669195, 0.02607843400609957, 0.0317045028266906, 0.7570556764755849, 0.02607843400619944, 0.3085734208212338, 0.6353194772067751, 0.03266098603990054, 0.635319477206719, 0.02344611593205012, 0.03266098604008431, 0.02344611593211183, 0.3085734208211992, 0.03266098603998926, 0.02841949702328586, 0.2801664445485925, 0.6575245517573061, 0.2801664445453317, 0.03388950667075014, 0.6575245517602716, 0.03388950667083485, 0.02841949702363653, 0.6575245517604836, 0.7288732494846533, 0.1160230818544248, 0.1355923645754941, 0.1160230818543114, 0.01951130408545599, 0.1355923645754723, 0.01951130408539934, 0.7288732494841443, 0.135592364576106, 0.144054962296286, 0.680860745134503, 0.1577793967317463, 0.01730489583748775, 0.1440549622963796, 0.1577793967318802, 0.6808607451345954, 0.01730489583762223, 0.157779396731644, 0.1579550425986422, 0.01216008923516082, 0.6749562807643449, 0.1549285874017353, 0.1579550425979384, 0.6749562807650454, 0.01216008923534246, 0.1549285874013449, 0.6749562807649698, 0.01001242225693271, 0.4226210458987284, 0.4504683351507885, 0.1168981966933887, 0.01001242225711663, 0.4504683351504025, 0.4226210458990461, 0.1168981966933672, 0.4504683351504458, 0.5566694034765993, 0.3465053726509192, 0.07740138872102775, 0.01942383515147987, 0.5566694034766593, 0.07740138872115238, 0.3465053726511215, 0.01942383515155061, 0.07740138872070887, 0.613355585987273, 0.03707747991970328, 0.3147249103897943, 0.037077479919721, 0.03484202370319439, 0.3147249103898168, 0.0348420237032176, 0.6133555859856407, 0.314724910391352, 0.3389609703820722, 0.01688864018974739, 0.280802863730256, 0.01688864018976847, 0.3633475256969008, 0.280802863730816, 0.3633475256976239, 0.3389609703821428, 0.2808028637303945, 0.2689933017087768, 0.1524656156298647, 0.02218921323001911, 0.5563518694315872, 0.2689933017084892, 0.0221892132299926, 0.1524656156298783, 0.5563518694313629, 0.02218921323010947, 0.3277711127242067, 0.3277711127240469, 0.01668666182758015, 0.4945020621274046, 0.02951774250327373, 0.2969980096590332, 0.02951774250352841, 0.1789821857098627, 0.2969980096594547, 0.1789821857092536, 0.4945020621265333, 0.2969980096607454, 0.1978395470663011, 0.02960272586703677, 0.2278827225106128, 0.02960272586707343, 0.544675004555141, 0.2278827225112254, 0.5446750045559754, 0.1978395470660405, 0.2278827225108745, 0.03229881331828368, 0.3371690700776922, 0.1320500662715863, 0.4984820503323908, 0.03229881331833241, 0.1320500662716444, 0.3371690700771687, 0.4984820503329197, 0.1320500662716443, 0.1207512536386751, 0.6778351364529377, 0.1011781353097973, 0.6778351364538195, 0.1002354745986857, 0.1011781353085159, 0.1002354745985345, 0.1207512536390304, 0.1011781353086743, 0.138106941917413, 0.3354607968315433, 0.03273663417213666, 0.4936956270790593, 0.1381069419173125, 0.03273663417222188, 0.3354607968314335, 0.4936956270789277, 0.03273663417220138, 0.2500532412376334, 0.2507616099201166, 0.4585809066512544, 0.2507616099209062, 0.04060424219100611, 0.4585809066503929, 0.04060424219112386, 0.2500532412374494, 0.4585809066512888, 0.09261674502310314, 0.3090936802353301, 0.5079960391158374, 0.3090936802365318, 0.0902935356263497, 0.5079960391138479, 0.09029353562658468, 0.09261674502301917, 0.5079960391137064, 0.1139695348123683, 0.1139695348116085, 0.6580913955636913, 0.1429946401664338, 0.2384743646845937, 0.1443488665029351, 0.4741821286458542, 0.1429946401665482, 0.1443488665032992, 0.2384743646842464, 0.4741821286455784, 0.1443488665038495, 0.2864949113772423, 0.1115967035331357, 0.1108203324715355, 0.1115967035330135, 0.4910880526179628, 0.1108203324717666, 0.4910880526178663, 0.286494911377532, 0.1108203324716498, 0.125536113167282, 0.1148821967817834, 0.2994703853758982, 0.4601113046750214, 0.1255361131668398, 0.2994703853761758, 0.1148821967814947, 0.4601113046733321, 0.299470385377665, 0.1968268645044536, 0.1968268645044899, 0.4095194064864269, 0.1275456273615645, 0.3107744236691848, 0.2609901451138755, 0.3107744236696469, 0.3006898038543989, 0.2609901451144841, 0.3006898038549698, 0.1275456273614465, 0.260990145113821, 0.296008469913471, 0.2960084699134847, 0.1119745902596869}; std::vector w = {0.00018520507764804083, 0.00024392157838708066, 0.000243921578397049, 0.000243921578397797, 0.0004426544798853663, 0.00044265447988711795, 0.000442654479888878, 0.00046562748965273685, 0.00046562748965351384, 0.00046562748965519116, 0.000527992042630873, 0.000527992042630885, 0.000527992042634342, 0.0005450543594569904, 0.000545054359463543, 0.0005450543594640881, 0.0007232685232691291, 0.0007232685232705268, 0.0007232685232867013, 0.000852258543339523, 0.0008522585433631999, 0.0008522585433649738, 0.0008570829478892283, 0.0008570829478907689, 0.0008570829479115868, 0.001012842250592951, 0.001012842250593049, 0.0010128422505964068, 0.0010128681282489697, 0.0010128681282520911, 0.001012868128252403, 0.0010556629969665655, 0.001055662996989173, 0.001055662996989902, 0.0010621542172256188, 0.0010621542172265183, 0.0010621542172285483, 0.0011697598043711175, 0.0011697598043717676, 0.0011697598043740865, 0.0012002940734976602, 0.001200294073497819, 0.0012002940735026382, 0.001204257126245662, 0.0012042571262564923, 0.0012042571262568921, 0.001384148614025617, 0.001384148614025973, 0.001384148614030434, 0.0015051557681943553, 0.0015051557681955198, 0.0015051557682016624, 0.0016274985904939964, 0.0016274985904981987, 0.0016274985905059345, 0.0018737072129849982, 0.00187370721298583, 0.0018737072129913232, 0.00193958466403963, 0.0019496326390221017, 0.0019496326390307033, 0.0019496326390484552, 0.00207150412472298, 0.0020715041247248048, 0.002071504124725327, 0.0021498066057806515, 0.002149806605781448, 0.0021498066057846865, 0.00253409922583179, 0.00253409922583231, 0.002534099225833395, 0.0025971204712147886, 0.002597120471219267, 0.002597120471221267, 0.0028928877153485666, 0.002892887715353863, 0.00289288771535987, 0.0029018056586740813, 0.0029018056586777047, 0.002901805658680232, 0.0029993712524892485, 0.003281967182103737, 0.0032819671821101765, 0.0032819671821297963, 0.0033417252382084, 0.0033417252382101736, 0.0033417252382157863, 0.0036081871202086548, 0.0036081871202103036, 0.0036081871202163014, 0.00450476478426666, 0.004597380383431189, 0.0045973803834320915, 0.00459738038343333, 0.004960765552103523}; return {std::move(x), std::move(w)}; } else if (m == 12) { // Xiao Gimbutas, 4 points, degree 12 std::vector x = {0.005336077019643312, 0.00533607701965122, 0.9839917689410563, 0.02289609338296681, 0.9546184848667337, 0.01790586600742269, 0.004579555742875841, 0.0228960933829682, 0.0179058660074233, 0.9546184848667292, 0.004579555742878448, 0.0179058660074248, 0.003634038039874281, 0.8589044391845247, 0.01981617795956102, 0.1176453448160352, 0.003634038039875694, 0.01981617795956089, 0.858904439184522, 0.1176453448160376, 0.01981617795956242, 0.03542041822023295, 0.5224715513554079, 0.01083320231284623, 0.5224715513554018, 0.4312748281115071, 0.01083320231285078, 0.43127482811151, 0.03542041822023723, 0.01083320231285082, 0.7939888499122254, 0.003557095377268929, 0.03305477917755645, 0.003557095377268694, 0.1693992755329503, 0.0330547791775573, 0.1693992755329477, 0.7939888499122263, 0.0330547791775572, 0.03490045658756769, 0.002190912398338879, 0.6722668687771673, 0.2906417622369189, 0.03490045658756785, 0.6722668687771728, 0.002190912398345305, 0.2906417622369231, 0.6722668687771658, 0.2341448840740503, 0.692501417588051, 0.0005245712210146334, 0.07282912711688336, 0.2341448840740487, 0.000524571221016469, 0.6925014175880473, 0.07282912711688572, 0.0005245712210174548, 0.3050609665772057, 0.01338905088310755, 0.02647035150983993, 0.6550796310298418, 0.3050609665772103, 0.02647035150984013, 0.01338905088311035, 0.6550796310298342, 0.02647035150984075, 0.004307832160712481, 0.09910993965985174, 0.1523437205162723, 0.09910993965985478, 0.7442385076631619, 0.1523437205162712, 0.7442385076631638, 0.004307832160714106, 0.1523437205162691, 0.05919699437842132, 0.07116099648149783, 0.01765696019083371, 0.07116099648149686, 0.8519850489492479, 0.01765696019083371, 0.851985048949244, 0.05919699437842153, 0.01765696019083469, 0.06123548744419532, 0.2324179989563869, 0.6975189009523872, 0.00882761264703291, 0.06123548744418655, 0.6975189009523861, 0.2324179989563872, 0.008827612647035132, 0.6975189009523897, 0.09851040146030474, 0.02461521681961515, 0.8525144716501452, 0.02461521681961598, 0.02435991006993637, 0.8525144716501353, 0.02435991006993612, 0.09851040146031514, 0.8525144716501323, 0.0299575541295916, 0.4833426975320538, 0.4764936746832871, 0.0102060736550687, 0.02995755412959167, 0.4764936746832857, 0.4833426975320467, 0.01020607365507009, 0.4764936746832923, 0.6941399725295285, 0.02362764131444302, 0.2643810815068189, 0.0178513046492101, 0.6941399725295325, 0.2643810815068127, 0.02362764131444548, 0.01785130464921, 0.2643810815068133, 0.03438582765934707, 0.02349501300003503, 0.0990866132622732, 0.02349501300003591, 0.8430325460783443, 0.09908661326227276, 0.8430325460783392, 0.03438582765934772, 0.09908661326227759, 0.3771063994176174, 0.5748727589136075, 0.0304912794861778, 0.5748727589136118, 0.01752956218259882, 0.03049127948617824, 0.01752956218260132, 0.3771063994176133, 0.03049127948617957, 0.3324969599766356, 0.3324969599766495, 0.002509120070074782, 0.1624334612403688, 0.3425319979501514, 0.4858624607394064, 0.3425319979501512, 0.009172080070074445, 0.4858624607394076, 0.009172080070074595, 0.1624334612403718, 0.48586246073941, 0.1021695690942893, 0.0288780033877441, 0.2474312436355633, 0.02887800338774448, 0.6215211838824055, 0.2474312436355594, 0.6215211838824011, 0.1021695690942831, 0.2474312436355717, 0.1183429008210087, 0.5459504609989174, 0.3155723028572613, 0.02013433532281294, 0.1183429008210102, 0.31557230285726, 0.5459504609989215, 0.02013433532281465, 0.3155723028572566, 0.4641610608049748, 0.08568157353024372, 0.4268281867514006, 0.0233291789133806, 0.4641610608049873, 0.4268281867513944, 0.08568157353024217, 0.02332917891338057, 0.4268281867513945, 0.4810426322698022, 0.2337921730578832, 0.2697819249825928, 0.01538326968972469, 0.4810426322698028, 0.2697819249825922, 0.2337921730578815, 0.01538326968972496, 0.2697819249825896, 0.08312515611213879, 0.6898395103699005, 0.02408109553674171, 0.20295423798122, 0.08312515611213996, 0.02408109553674232, 0.6898395103698945, 0.2029542379812216, 0.02408109553674225, 0.02742002486773186, 0.1526139134707651, 0.7016489041188455, 0.1526139134707658, 0.1183171575426507, 0.7016489041188496, 0.1183171575426545, 0.02742002486773273, 0.7016489041188465, 0.0256118593510928, 0.6956093147224817, 0.1154478712992354, 0.1633309546271897, 0.02561185935109283, 0.1154478712992363, 0.695609314722476, 0.1633309546271908, 0.1154478712992393, 0.2708902039289692, 0.5606658837700146, 0.1501625601277951, 0.5606658837700202, 0.01828135217322237, 0.1501625601277912, 0.01828135217322301, 0.2708902039289658, 0.1501625601277926, 0.6014420504489564, 0.07656778420928108, 0.06193417797899343, 0.2600559873627669, 0.6014420504489668, 0.06193417797899366, 0.0765677842092839, 0.2600559873627518, 0.06193417797899652, 0.07462605132720868, 0.128036327678861, 0.0802610178929508, 0.7170766031009705, 0.0746260513272115, 0.08026101789294995, 0.1280363276788657, 0.7170766031009719, 0.08026101789294988, 0.1859004086837668, 0.01925270365058326, 0.5025986233550219, 0.2922482643106227, 0.185900408683766, 0.5025986233550276, 0.01925270365058409, 0.2922482643106334, 0.5025986233550225, 0.1176843397145563, 0.1176843397145589, 0.6469469808563281, 0.2114376345514575, 0.2363780333382985, 0.02542476511605016, 0.5267595669941891, 0.211437634551462, 0.02542476511605067, 0.2363780333382918, 0.5267595669941991, 0.02542476511605207, 0.4463155182649424, 0.4028816246284508, 0.02619956075625247, 0.402881624628445, 0.1246032963503558, 0.02619956075625247, 0.1246032963503482, 0.4463155182649512, 0.02619956075625223, 0.06201821930268873, 0.08004502188860088, 0.5617139637207647, 0.2962227950879483, 0.06201821930269108, 0.5617139637207541, 0.08004502188860683, 0.2962227950879417, 0.5617139637207641, 0.2972901553171066, 0.3803360632262429, 0.297442217416377, 0.3803360632262462, 0.02493156404027269, 0.2974422174163724, 0.02493156404027382, 0.2972901553171099, 0.297442217416373, 0.6021485466361711, 0.0799434865871736, 0.1972600853341152, 0.120647881442539, 0.6021485466361716, 0.1972600853341229, 0.07994348658717083, 0.1206478814425371, 0.1972600853341221, 0.02875584715397042, 0.4891098147268006, 0.1151244443817293, 0.3670098937375008, 0.02875584715397076, 0.1151244443817276, 0.4891098147267995, 0.3670098937374994, 0.1151244443817297, 0.134993927609458, 0.4106933140901517, 0.3647602491933826, 0.08955250910700893, 0.1349939276094596, 0.3647602491933785, 0.4106933140901513, 0.08955250910701389, 0.3647602491933695, 0.3918753971997565, 0.114767516763099, 0.2334539319513102, 0.2599031540858343, 0.3918753971997636, 0.2334539319513043, 0.1147675167631061, 0.2599031540858358, 0.2334539319513023, 0.1301270947620232, 0.3163617622563091, 0.1273831687260135, 0.3163617622563013, 0.426127974255657, 0.1273831687260152, 0.4261279742556524, 0.1301270947620246, 0.127383168726021, 0.4045023685632761, 0.2089114968404676, 0.2918235155672437, 0.2089114968404671, 0.09476261902901918, 0.2918235155672501, 0.0947626190290201, 0.4045023685632663, 0.2918235155672511, 0.2274485368045121, 0.1002497543721358, 0.4514599495099783, 0.1002497543721362, 0.2208417593133712, 0.4514599495099766, 0.2208417593133764, 0.2274485368045148, 0.4514599495099708, 0.5075486051202548, 0.2316476952172538, 0.1266237132343918, 0.1341799864280956, 0.5075486051202593, 0.126623713234395, 0.231647695217251, 0.1341799864280973, 0.1266237132343931, 0.3053295843360563, 0.305329584336061, 0.08401124699181982, 0.2482123715562289, 0.2482123715562241, 0.2553628853313281}; std::vector w = {3.336665703934045e-05, 8.429047994003496e-05, 8.42904799400373e-05, 8.429047994005044e-05, 0.0002149093453616615, 0.000214909345361668, 0.00021490934536169885, 0.0003477168822065943, 0.0003477168822067472, 0.00034771688220676085, 0.00034938896941761667, 0.0003493889694176177, 0.00034938896941761986, 0.00036951072812999795, 0.00036951072813002267, 0.00036951072813010886, 0.00043580724293473854, 0.0004358072429347666, 0.00043580724293479047, 0.0004693930811221192, 0.00046939308112213947, 0.000469393081122183, 0.00047266648548842713, 0.0004726664854884277, 0.00047266648548845966, 0.0005211217875707378, 0.0005211217875707438, 0.0005211217875707549, 0.0005256622670893608, 0.0005256622670894092, 0.0005256622670894917, 0.0005271978638527369, 0.0005271978638527777, 0.0005271978638527842, 0.0005369300725478063, 0.0005369300725478469, 0.000536930072547865, 0.0005566045279494747, 0.0005566045279495304, 0.000556604527949537, 0.0006129569463521267, 0.0006129569463521502, 0.0006129569463521515, 0.0007008690706483745, 0.0007008690706484037, 0.0007008690706485162, 0.0008238259069470653, 0.0008887153590631331, 0.0008887153590632215, 0.0008887153590632422, 0.0010498461857128857, 0.0010498461857129228, 0.0010498461857129625, 0.0012296728163221301, 0.0012296728163221822, 0.0012296728163222826, 0.0012860526386169384, 0.0012860526386169403, 0.0012860526386169514, 0.0013011726613118815, 0.0013011726613120303, 0.0013011726613120442, 0.0013077514257282738, 0.0013077514257283426, 0.0013077514257283478, 0.001333432670675809, 0.0013334326706758266, 0.001333432670675831, 0.0013859680589182145, 0.0013859680589182338, 0.0013859680589183101, 0.0014030871341772979, 0.0014030871341773462, 0.0014030871341773807, 0.0014306258422900732, 0.0014306258422900986, 0.0014306258422901264, 0.0014481262251185862, 0.0014481262251186703, 0.0014481262251187074, 0.0015201310181193032, 0.0015201310181193215, 0.001520131018119335, 0.0018509975122165715, 0.0018566467954329133, 0.0018566467954329567, 0.00185664679543301, 0.0019099333823615115, 0.0019099333823615501, 0.001909933382361565, 0.001925296724080295, 0.0019252967240803384, 0.0019252967240803549, 0.0019506389267458684, 0.0019506389267458799, 0.00195063892674594, 0.001981330865268667, 0.001981330865268715, 0.0019813308652687485, 0.00202612293640226, 0.0020261229364022834, 0.0020261229364023216, 0.0021899086547603466, 0.002189908654760405, 0.0021899086547604815, 0.0025877797901108766, 0.0025877797901111468, 0.0025877797901113883, 0.0028180784812232604, 0.0028180784812233983, 0.0028180784812235748, 0.0031305630728214965, 0.0031305630728215637, 0.0031305630728215767, 0.003251426805132802, 0.003251426805132915, 0.0032514268051329347, 0.003658706854740727, 0.0036587068547407633, 0.003658706854740793, 0.004252858800216373, 0.0049174945629996405}; return {std::move(x), std::move(w)}; } else if (m == 13) { // Xiao Gimbutas, 4 points, degree 13 std::vector x = {0.01034510725658796, 0.01034510725652366, 0.9689646782302881, 0.9412811493523093, 0.008913012362466612, 0.03023687451099998, 0.008913012362483565, 0.01956896377423934, 0.03023687451100695, 0.01956896377427091, 0.9412811493522358, 0.03023687451100657, 0.008603145262962948, 0.8844920682248393, 0.02544900390361993, 0.08145578260860413, 0.008603145262975986, 0.02544900390362636, 0.8844920682247885, 0.08145578260858234, 0.02544900390363992, 0.07915594917543556, 0.87881874683591, 0.008412158658388869, 0.03361314533024481, 0.07915594917539193, 0.008412158658414975, 0.8788187468359566, 0.03361314533023658, 0.008412158658423473, 0.02072452926120901, 0.2820938675417852, 0.005896688536536578, 0.6912849146605238, 0.02072452926121111, 0.005896688536550856, 0.282093867541703, 0.691284914660533, 0.005896688536556295, 0.660612504418027, 0.00423850950551799, 0.3059064193536999, 0.004238509505525088, 0.02924256672275331, 0.305906419353717, 0.02924256672275465, 0.660612504417956, 0.3059064193537546, 0.8108726651448125, 0.03145181004767937, 0.1465204590611696, 0.01115506574634481, 0.8108726651448392, 0.1465204590611652, 0.03145181004765925, 0.01115506574634996, 0.1465204590611479, 0.01337142894352578, 0.0611452791183259, 0.1074571297438097, 0.06114527911829663, 0.8180261621942968, 0.1074571297438493, 0.8180261621944124, 0.01337142894356406, 0.1074571297437935, 0.007414460977235957, 0.6955288748491347, 0.02821381240546334, 0.6955288748491215, 0.268842851768176, 0.02821381240546195, 0.2688428517681751, 0.007414460977257575, 0.02821381240545042, 0.02477429690597821, 0.007482222177392445, 0.5213287809539923, 0.007482222177401224, 0.4464146999625711, 0.5213287809540651, 0.4464146999626367, 0.02477429690598193, 0.5213287809539731, 0.02112965969938043, 0.09035183340349828, 0.86581765678534, 0.09035183340359541, 0.02270085011179018, 0.8658176567852218, 0.02270085011179335, 0.0211296596993924, 0.8658176567851917, 0.3417930742320212, 0.002594165265187326, 0.5797685345851115, 0.002594165265210041, 0.07584422591768267, 0.5797685345850755, 0.07584422591769545, 0.3417930742320845, 0.579768534585002, 0.4847746571587412, 0.4810641377028125, 0.01833057078756236, 0.4810641377027924, 0.01583063435090152, 0.01833057078755179, 0.0158306343508983, 0.4847746571587827, 0.01833057078756206, 0.1676137196659997, 0.7086431089234964, 0.002914466641356441, 0.7086431089235703, 0.1208287047691087, 0.002914466641385641, 0.1208287047691088, 0.1676137196659543, 0.002914466641410491, 0.05182981415363299, 0.7671835222425682, 0.01275903790005842, 0.767183522242537, 0.1682276257037641, 0.01275903790006475, 0.1682276257037143, 0.05182981415365493, 0.01275903790008807, 0.1603808640957398, 0.780025722472745, 0.04144316733981209, 0.01815024609171124, 0.1603808640958211, 0.04144316733977616, 0.7800257224727031, 0.0181502460917103, 0.04144316733980475, 0.001030437977338548, 0.3006497694477112, 0.5608693977782155, 0.3006497694476872, 0.1374503947967453, 0.560869397778206, 0.1374503947967274, 0.00103043797737723, 0.5608693977782493, 0.009571364698298758, 0.6068954673005267, 0.3058952472331785, 0.6068954673005383, 0.07763792076797839, 0.3058952472331658, 0.07763792076798627, 0.009571364698318083, 0.3058952472331321, 0.2359081252750476, 0.02712491103418074, 0.7148030256628792, 0.02712491103418163, 0.02216393802789915, 0.7148030256628535, 0.02216393802790523, 0.2359081252749586, 0.7148030256629532, 0.5611854641624137, 0.3497758718670665, 0.01001327495242358, 0.07902538901810821, 0.5611854641624555, 0.01001327495242865, 0.3497758718669661, 0.0790253890181477, 0.01001327495243759, 0.01201591051800159, 0.7035601418166272, 0.1282324135027442, 0.1561915341626666, 0.01201591051801169, 0.1282324135027487, 0.7035601418165589, 0.1561915341626685, 0.1282324135027578, 0.01227254816726416, 0.1520983651999485, 0.1736456691583085, 0.1520983651998947, 0.6619834174744651, 0.1736456691583576, 0.6619834174745407, 0.01227254816727948, 0.1736456691582867, 0.009608952348337692, 0.5009662598450281, 0.2173473462639225, 0.5009662598449669, 0.2720774415427657, 0.21734734626392, 0.2720774415427986, 0.009608952348355002, 0.2173473462639029, 0.117142028395487, 0.1306252415276769, 0.7370755718293556, 0.01515715824748938, 0.1171420283955028, 0.7370755718293308, 0.1306252415276693, 0.01515715824751321, 0.7370755718293365, 0.06596911170718446, 0.4869178929175357, 0.4164421277991943, 0.03067086757606238, 0.06596911170715442, 0.4164421277993404, 0.4869178929174335, 0.03067086757608414, 0.4164421277992895, 0.06180743725428443, 0.7796220928971531, 0.07217289794213186, 0.08639757190638243, 0.06180743725431112, 0.07217289794220663, 0.7796220928970106, 0.08639757190646453, 0.07217289794218502, 0.2124417592665611, 0.3704425559581763, 0.01068152306355014, 0.4064341617117311, 0.2124417592666155, 0.01068152306356992, 0.3704425559580568, 0.4064341617118274, 0.01068152306357016, 0.01757754194097423, 0.3170690280570044, 0.0808011880664319, 0.3170690280569446, 0.5845522419356186, 0.08080118806646398, 0.584552241935574, 0.017577541940982, 0.0808011880664406, 0.4819714524309398, 0.01756230923632635, 0.3414320148667813, 0.1590342234659364, 0.4819714524309074, 0.3414320148668196, 0.01756230923633349, 0.1590342234659289, 0.3414320148668064, 0.08902566699068729, 0.3386042162630997, 0.02525485039503888, 0.3386042162631206, 0.5471152663511726, 0.02525485039504986, 0.5471152663511358, 0.08902566699071175, 0.02525485039503578, 0.05285544098330982, 0.4000979296301265, 0.4641321089536249, 0.4000979296298913, 0.0829145204330444, 0.4641321089536999, 0.08291452043303993, 0.05285544098335554, 0.4641321089535966, 0.01782606090077854, 0.2874706358928882, 0.3856172937149586, 0.2874706358928806, 0.3090860094913717, 0.3856172937149701, 0.3090860094913699, 0.01782606090078187, 0.3856172937149664, 0.3710934105995327, 0.02657234084597485, 0.09690187156313018, 0.02657234084597537, 0.5054323769913355, 0.09690187156315631, 0.5054323769913123, 0.371093410599567, 0.09690187156314264, 0.02148831270206694, 0.3206072988650484, 0.2127349091997354, 0.4451694792332048, 0.02148831270207087, 0.2127349091997207, 0.3206072988650124, 0.445169479233177, 0.2127349091997361, 0.0951170007288927, 0.0951170007290063, 0.7146489978132146, 0.1735802358450919, 0.02890436352483536, 0.3570824842180014, 0.4404329164120814, 0.1735802358450641, 0.3570824842179925, 0.02890436352488776, 0.4404329164119998, 0.357082484218023, 0.08023850116226482, 0.6174866483249893, 0.07195784522707145, 0.6174866483249148, 0.2303170052857254, 0.0719578452270639, 0.2303170052856326, 0.08023850116232735, 0.07195784522713053, 0.09452881219751588, 0.1751776752009559, 0.07065601264629488, 0.1751776752009207, 0.6596374999553175, 0.07065601264621812, 0.6596374999552117, 0.09452881219755312, 0.0706560126462567, 0.06541766890798655, 0.0754347492130809, 0.2206728628957595, 0.6384747189831496, 0.0654176689080234, 0.2206728628957641, 0.07543474921303878, 0.6384747189831916, 0.22067286289572, 0.5321359768315169, 0.2362423058619533, 0.03066111663210869, 0.2362423058618978, 0.2009606006744852, 0.03066111663211837, 0.200960600674406, 0.5321359768315502, 0.03066111663213887, 0.2434135622407882, 0.1003198536332518, 0.5906059332310526, 0.1003198536332653, 0.06566065089495007, 0.5906059332310177, 0.06566065089490132, 0.2434135622409514, 0.590605933230827, 0.03545951407500253, 0.1645691312152424, 0.5385515468421933, 0.2614198078675792, 0.03545951407500569, 0.5385515468421994, 0.1645691312152262, 0.2614198078675275, 0.5385515468422312, 0.078850996951403, 0.2214791068336956, 0.1663421652944799, 0.5333277309204181, 0.07885099695144153, 0.1663421652944797, 0.2214791068336244, 0.533327730920471, 0.1663421652944595, 0.0773186488433034, 0.5438135338904002, 0.1960861755130517, 0.1827816417531721, 0.0773186488433082, 0.1960861755131724, 0.5438135338903273, 0.1827816417532548, 0.1960861755130822, 0.1668632870416709, 0.1668632870415595, 0.4994101388751889, 0.1227490628669584, 0.3909296074394038, 0.08756388655247146, 0.3987574431411315, 0.1227490628669686, 0.0875638865525005, 0.3909296074393537, 0.3987574431411711, 0.08756388655250151, 0.276668775818374, 0.2188997970179869, 0.4020005482407241, 0.2188997970179731, 0.1024308789229114, 0.4020005482406983, 0.1024308789229648, 0.2766687758182949, 0.4020005482407235, 0.3095550183430513, 0.3095550183430962, 0.07133494497075156, 0.3138415213429039, 0.09663461813535858, 0.2305808594445332, 0.3589430010771437, 0.3138415213429264, 0.230580859444564, 0.09663461813536428, 0.3589430010771935, 0.2305808594445379, 0.4418055888803888, 0.2069223626302676, 0.1404348940839113, 0.210837154405392, 0.4418055888803925, 0.1404348940839597, 0.206922362630213, 0.2108371544054255, 0.1404348940839574, 0.1108495022995848, 0.1626179922026075, 0.3263197105354871, 0.4002127949623002, 0.1108495022995989, 0.3263197105354845, 0.1626179922025704, 0.4002127949623043, 0.326319710535522, 0.2505588869395824, 0.2505588869395491, 0.2483233391813068}; std::vector w = {4.134324458614672e-05, 0.00010841532703717533, 0.00010841532703736251, 0.00010841532703751804, 0.00021516249282564702, 0.00021516249282587435, 0.00021516249282610783, 0.00022347525797821485, 0.0002234752579784065, 0.00022347525797851097, 0.00023853469157155934, 0.00023853469157180234, 0.00023853469157185316, 0.00033613166424808783, 0.0003361316642481367, 0.00033613166424835715, 0.0003480651020245443, 0.0003480651020245613, 0.00034806510202475897, 0.00035512259072977237, 0.00035512259073010164, 0.0003551225907301965, 0.00035608395080367433, 0.0003560839508037468, 0.00035608395080396967, 0.0003691445658330918, 0.0003691445658331333, 0.000369144565833457, 0.000371173748022265, 0.0003711737480225677, 0.00037117374802262765, 0.00040213122869147067, 0.000402131228691888, 0.00040213122869227303, 0.0004154352296915045, 0.000415435229691703, 0.0004154352296917142, 0.000426698597924656, 0.0004266985979252545, 0.0004266985979257583, 0.0004530237838723606, 0.0004530237838724362, 0.0004530237838729462, 0.0005183573574952897, 0.0005183573574953276, 0.0005183573574954048, 0.0005341727061933349, 0.0005341727061939642, 0.0005341727061944354, 0.0006550908587339017, 0.0006550908587343363, 0.0006550908587344126, 0.0006619235298199111, 0.0006619235298200495, 0.0006619235298204403, 0.0006904621960691755, 0.0006904621960692998, 0.0006904621960698748, 0.0007517374404992122, 0.0007517374404998534, 0.0007517374404998906, 0.0007731049469574419, 0.0007731049469581392, 0.0007731049469581534, 0.0007934548044526308, 0.0007934548044530055, 0.0007934548044531542, 0.0008002620463692416, 0.000800262046369655, 0.0008002620463703901, 0.0008144758584702261, 0.0008144758584703776, 0.0008144758584716199, 0.0009212791124099626, 0.0009212791124102366, 0.0009212791124106396, 0.0009317064412002208, 0.0009317064412010393, 0.0009317064412011602, 0.000938800900399439, 0.0009388009003994993, 0.0009388009003998087, 0.0011277704236273554, 0.0011277704236277617, 0.0011277704236277934, 0.0011558578573166221, 0.001155857857316737, 0.0011558578573169188, 0.001177874388273861, 0.001177874388275692, 0.0011778743882767226, 0.00126388507718553, 0.0012638850771856667, 0.001263885077185743, 0.00130986012194251, 0.0013098601219425701, 0.0013098601219427271, 0.0013824952238897048, 0.0013824952238897677, 0.0013824952238899353, 0.0014385650798460579, 0.0015235584390117577, 0.0015235584390132365, 0.001523558439014583, 0.001553388089457361, 0.001553388089457734, 0.0015533880894588244, 0.0015785619534530034, 0.0015785619534532253, 0.0015785619534532372, 0.0016097788682893919, 0.001609778868289749, 0.0016097788682897544, 0.0016802161392982585, 0.00168021613929846, 0.0016802161392987117, 0.0017764529129038432, 0.00177645291290503, 0.0017764529129061567, 0.0018087386127984568, 0.0018087386127986667, 0.0018087386127988634, 0.0019104583647188483, 0.0019104583647194969, 0.00191045836472014, 0.0021274466568828383, 0.00212744665688324, 0.002127446656884127, 0.0023400227806175985, 0.00256011618791495, 0.0025601161879153333, 0.0025601161879153585, 0.00257113625207729, 0.0025711362520778134, 0.0025711362520780268, 0.002834280437245222, 0.002961688555453913, 0.002961688555454135, 0.002961688555454362, 0.0031167609951133716, 0.003116760995113398, 0.0031167609951139037, 0.003303366568208123, 0.00330336656820916, 0.0033033665682094884, 0.004303940769897278}; return {std::move(x), std::move(w)}; } else if (m == 14) { // Xiao Gimbutas, 4 points, degree 14 std::vector x = {0.006857703680959708, 0.006857703681460325, 0.9794268889578419, 0.009252353846064727, 0.004788844763930928, 0.03329961033004269, 0.9526591910590293, 0.009252353846425697, 0.03329961033024104, 0.004788844764298505, 0.9526591910584257, 0.03329961033063356, 0.06141444425249755, 0.9232999651585443, 0.002010058648387674, 0.9232999651574737, 0.01327553194083024, 0.002010058649115951, 0.01327553194070744, 0.06141444425278526, 0.002010058649810132, 0.01773414386230762, 0.8989650605386635, 0.01446762164021698, 0.8989650605385748, 0.0688331739588598, 0.01446762164046387, 0.06883317395833545, 0.01773414386215409, 0.01446762164064887, 0.009247363617832223, 0.3990139062271814, 0.5829037436121112, 0.399013906225403, 0.008834986543019535, 0.5829037436136701, 0.008834986543086735, 0.009247363618203858, 0.5829037436168685, 0.7709900229262808, 0.01952291262766601, 0.01015620879258834, 0.01952291262769855, 0.1993308556536677, 0.01015620879265931, 0.1993308556534236, 0.7709900229262199, 0.01015620879270446, 0.02032554506145879, 0.7220074978401071, 0.2431115808353706, 0.7220074978408385, 0.01455537626308008, 0.2431115808345355, 0.01455537626344418, 0.02032554506140792, 0.2431115808356294, 0.01883099256369943, 0.01956534940768639, 0.8819163274688043, 0.07968733056157902, 0.01883099256402238, 0.8819163274665061, 0.01956534940798839, 0.07968733056254174, 0.8819163274654277, 0.8343838905775879, 0.02228963644433849, 0.1242803391217787, 0.02228963644438797, 0.0190461338562963, 0.1242803391208842, 0.01904613385630611, 0.8343838905759399, 0.1242803391233692, 0.2039231912002361, 0.02012121561803056, 0.01440053742030228, 0.02012121561804498, 0.7615550557610796, 0.01440053742029756, 0.7615550557609511, 0.2039231912006661, 0.01440053742036038, 0.3361509636180879, 0.384645703604847, 0.2764557088059904, 0.002747623971354991, 0.3361509636165141, 0.2764557088037994, 0.3846457036073966, 0.002747623971615903, 0.276455708804184, 0.350859996445968, 0.5522182104345288, 0.004931325226067042, 0.5522182104342734, 0.09199046789365573, 0.004931325226427101, 0.09199046789304373, 0.3508599964450814, 0.004931325226952402, 0.02152082553450803, 0.0160803500126914, 0.379120808464382, 0.5832780159922265, 0.02152082553456097, 0.3791208084604637, 0.01608035001313742, 0.5832780159937186, 0.379120808458791, 0.00323980932956962, 0.08400887661431675, 0.386296150270937, 0.5264551637848466, 0.003239809329937143, 0.3862961502712207, 0.0840088766149807, 0.5264551637842712, 0.3862961502705597, 0.09120718428587543, 0.00411013141124883, 0.3989904928634934, 0.004110131411517128, 0.5056921914391684, 0.3989904928650359, 0.5056921914383363, 0.09120718428557456, 0.3989904928644713, 0.5156439635468597, 0.2349406124884029, 0.2474912783867316, 0.2349406124899761, 0.001924145578256931, 0.2474912783858402, 0.001924145578230063, 0.5156439635453917, 0.2474912783874761, 0.01932237274818349, 0.07638832520527908, 0.05638385444735509, 0.8479054475994247, 0.01932237274817627, 0.05638385444701339, 0.07638832520496093, 0.8479054476003125, 0.05638385444661988, 0.3823009464917305, 0.0167988939596847, 0.02255542412116617, 0.01679889395970684, 0.5783447354272748, 0.02255542412126642, 0.5783447354271684, 0.3823009464918677, 0.02255542412122661, 0.2129425371446761, 0.0212807476383709, 0.7437074210582457, 0.02206929415866949, 0.2129425371459558, 0.7437074210569653, 0.02128074763856252, 0.02206929415888909, 0.7437074210601419, 0.3317239526150014, 0.3317239526139896, 0.004828142155987282, 0.5404797680657268, 0.007294355381304199, 0.2453763152229318, 0.007294355381551722, 0.2068495613301014, 0.2453763152216877, 0.2068495613312503, 0.5404797680654154, 0.2453763152219601, 0.8058111958219785, 0.09857704079908997, 0.07586181203272872, 0.01974995134613911, 0.8058111958226273, 0.07586181203231102, 0.09857704079958525, 0.0197499513461522, 0.07586181203295679, 0.01748287896089844, 0.3792756315222444, 0.02304572711563008, 0.3792756315220148, 0.5801957624014105, 0.02304572711563284, 0.5801957624013359, 0.01748287896096796, 0.02304572711565241, 0.2874224758303754, 0.005601417694663574, 0.6029413319659248, 0.1040347745094613, 0.2874224758296852, 0.6029413319661477, 0.005601417694883342, 0.104034774509157, 0.6029413319657438, 0.1114551583386971, 0.01030235261682538, 0.7627564092743483, 0.01030235261707004, 0.1154860797700707, 0.7627564092740204, 0.1154860797697298, 0.1114551583385124, 0.7627564092743049, 0.2935488505770338, 0.09761999738538321, 0.6006213514676992, 0.09761999738502115, 0.008209800569967951, 0.6006213514669282, 0.008209800569969635, 0.293548850577753, 0.6006213514668876, 0.08497804426593077, 0.517513525717932, 0.01294633315160566, 0.5175135257172315, 0.3845620968650357, 0.01294633315184834, 0.3845620968647721, 0.08497804426566612, 0.01294633315189131, 0.7877678986243767, 0.09409500064304101, 0.01931078849337184, 0.09882631223971038, 0.7877678986241535, 0.01931078849337398, 0.09409500064289474, 0.09882631223940282, 0.0193107884933751, 0.5497595632204098, 0.01252905980804105, 0.1219406461222866, 0.01252905980810353, 0.3157707308500686, 0.1219406461215593, 0.3157707308512963, 0.5497595632185667, 0.1219406461220274, 0.5303977839664743, 0.2336768622507286, 0.008568966905135528, 0.2273563868775282, 0.5303977839660673, 0.008568966905105817, 0.2336768622504743, 0.2273563868768414, 0.008568966905196002, 0.01341116464697127, 0.6612982036782233, 0.09601282934079287, 0.2292778023349974, 0.01341116464707565, 0.0960128293404185, 0.661298203678323, 0.2292778023338679, 0.09601282934065727, 0.6533880188663116, 0.2247326755186619, 0.01699262706566816, 0.10488667854954, 0.6533880188669758, 0.01699262706582885, 0.2247326755185866, 0.1048866785488491, 0.01699262706599834, 0.2612596694959481, 0.007667507939535023, 0.4304413398190844, 0.3006314827447682, 0.2612596694952175, 0.430441339820285, 0.007667507939682639, 0.3006314827445465, 0.4304413398197249, 0.01940675068598702, 0.1990033699311775, 0.07543662479224182, 0.7061532545902, 0.01940675068596605, 0.07543662479225494, 0.1990033699315986, 0.706153254589724, 0.07543662479267595, 0.2153693225142795, 0.07162380480079734, 0.0661407842999457, 0.07162380480015625, 0.6468660883848494, 0.06614078429932518, 0.6468660883845436, 0.2153693225150791, 0.06614078429928018, 0.077815923168826, 0.07781592316817382, 0.7665522304918784, 0.01877419874737987, 0.1014405096223781, 0.1843049531492825, 0.6954803384805778, 0.01877419874750536, 0.1843049531493682, 0.1014405096223557, 0.6954803384805676, 0.1843049531495559, 0.09578855271960066, 0.01823203771678103, 0.2102844818945803, 0.6756949276694415, 0.09578855271908891, 0.2102844818946296, 0.01823203771688221, 0.6756949276687967, 0.2102844818947134, 0.4739258549152569, 0.394299156898155, 0.1145335941286789, 0.394299156900111, 0.01724139405791831, 0.1145335941285089, 0.01724139405793893, 0.4739258549146191, 0.1145335941289155, 0.09983516167455454, 0.2235331462354234, 0.02642159417479953, 0.6502100979152713, 0.09983516167429868, 0.02642159417490681, 0.2235331462366786, 0.650210097913948, 0.026421594174992, 0.04604965750902205, 0.3968682475417654, 0.5163227682090831, 0.3968682475411293, 0.0407593267406208, 0.5163227682092674, 0.04075932674082398, 0.04604965750936901, 0.5163227682088478, 0.5706340243899409, 0.08400311872986549, 0.105036530907377, 0.240326325971861, 0.5706340243897189, 0.1050365309082531, 0.08400311873016891, 0.240326325974372, 0.1050365309055699, 0.1932367143291832, 0.0713650912511827, 0.6582528796899906, 0.07714531472941318, 0.1932367143282536, 0.6582528796907257, 0.07136509125209903, 0.07714531472977029, 0.658252879686536, 0.07837424052530441, 0.3761818136931925, 0.07236155120489983, 0.4730823945784287, 0.07837424052573189, 0.07236155120465555, 0.3761818136912941, 0.473082394578608, 0.07236155120451863, 0.08923568468861766, 0.5115423865329087, 0.09281715399980724, 0.3064047747789605, 0.08923568468759871, 0.09281715400051348, 0.5115423865325862, 0.3064047747787268, 0.09281715400041667, 0.09737203363634417, 0.1007344014121848, 0.4929088721328376, 0.3089846928173871, 0.09737203363532915, 0.492908872135997, 0.1007344014106231, 0.3089846928177313, 0.4929088721369547, 0.1775478833143351, 0.3899676091564037, 0.4030241508000952, 0.3899676091567567, 0.02946035672918423, 0.4030241508003236, 0.02946035672919241, 0.1775478833136099, 0.403024150799614, 0.0382221895291049, 0.2034004094915035, 0.5628552072214578, 0.195522193757886, 0.03822218952882864, 0.5628552072215468, 0.2034004094916, 0.1955221937576974, 0.5628552072214532, 0.391993350183357, 0.1914181781007838, 0.03060828252772313, 0.3859801891875482, 0.3919933501838906, 0.03060828252764542, 0.1914181781009923, 0.3859801891869217, 0.03060828252764046, 0.03830546710979171, 0.4143211516037891, 0.3863688156730401, 0.4143211516027137, 0.1610045656138734, 0.3863688156733209, 0.1610045656134992, 0.0383054671100454, 0.3863688156731704, 0.2056632530541998, 0.05411126130896651, 0.2061145172704723, 0.5341109683662314, 0.2056632530540627, 0.2061145172707037, 0.05411126130928814, 0.5341109683657477, 0.2061145172697658, 0.1015171591699777, 0.1020634349618442, 0.1162724881045504, 0.6801469177635077, 0.1015171591702443, 0.1162724881044248, 0.102063434961655, 0.6801469177636642, 0.1162724881042698, 0.06794709903225467, 0.08050368241686043, 0.3036135923650056, 0.5479356261852021, 0.0679470990326355, 0.3036135923649413, 0.08050368241747642, 0.5479356261849525, 0.3036135923645354, 0.2160065443462191, 0.5071777807646763, 0.2145116281103287, 0.5071777807651786, 0.06230404677880914, 0.2145116281099157, 0.06230404677908077, 0.2160065443456221, 0.2145116281088852, 0.1271466762690916, 0.167752470283245, 0.3465563262838002, 0.1677524702817992, 0.358544527163581, 0.3465563262852937, 0.3585445271645286, 0.1271466762697583, 0.3465563262840383, 0.3502614515179938, 0.04637577761701088, 0.2412387153575752, 0.3621240555078011, 0.3502614515181501, 0.2412387153574093, 0.04637577761704335, 0.3621240555066351, 0.2412387153578485, 0.5035445472625852, 0.2096968537717866, 0.07908151868224303, 0.2076770802830252, 0.5035445472625711, 0.07908151868236299, 0.2096968537719228, 0.207677080282593, 0.07908151868295447, 0.09704125425919986, 0.2851510675157584, 0.3639850895836558, 0.2851510675153835, 0.2538225886425896, 0.363985089582452, 0.2538225886423787, 0.09704125425935214, 0.3639850895833628, 0.3029181193293014, 0.3029181193293765, 0.09124564201234239, 0.4526488719953613, 0.1783434906135457, 0.2141553642703896, 0.1783434906131041, 0.1548522731197198, 0.2141553642717896, 0.1548522731205867, 0.4526488719948076, 0.2141553642709648, 0.165887520255092, 0.165887520255045, 0.5023374392351683, 0.1538060360803229, 0.3322960833854866, 0.1644327097770143, 0.332296083385749, 0.3494651707568161, 0.1644327097772045, 0.3494651707565804, 0.1538060360803426, 0.1644327097771141, 0.2432210319415151, 0.2432210319416702, 0.2703369041747087}; std::vector w = {2.2105078592571967e-05, 5.104037863193469e-05, 5.1040378634203715e-05, 5.104037863543036e-05, 6.442538948289391e-05, 6.442538948628821e-05, 6.442538948864303e-05, 0.0001664251559695554, 0.00016642515597055306, 0.00016642515597261462, 0.00016751105250402333, 0.00016751105250589703, 0.00016751105250894984, 0.0002517305490836875, 0.00025173054908493396, 0.000251730549085241, 0.0002595061872553382, 0.00025950618726126, 0.0002595061872718605, 0.00026235085677183735, 0.00026235085677843987, 0.0002623508567806215, 0.0002896234674114693, 0.00028962346741212385, 0.00028962346741429767, 0.00032793268910043645, 0.0003279326891004587, 0.0003279326891011032, 0.00033555287853835234, 0.0003355528785454428, 0.0003355528785475206, 0.00033972253178854585, 0.0003397225317983497, 0.00033972253180672413, 0.0003441188150776556, 0.000344118815078894, 0.00034411881507963863, 0.0003761105389994102, 0.0003761105390067088, 0.00037611053901267236, 0.0003795403780601865, 0.00037954037806408534, 0.00037954037806870296, 0.0004344437592961113, 0.00043444375930333496, 0.00043444375930493687, 0.00043682384903025753, 0.000436823849034062, 0.00043682384903561935, 0.0004725137441075373, 0.00047251374410967945, 0.0004725137441096982, 0.0004780758505185763, 0.0004780758505195665, 0.0004780758505243325, 0.0004793254756053175, 0.000505119620061448, 0.0005051196200666875, 0.0005051196200669975, 0.0005119272825804478, 0.0005119272825807408, 0.0005119272825811382, 0.0005134274075967697, 0.0005134274075983005, 0.0005134274075996252, 0.000515837164893621, 0.0005158371648967786, 0.0005158371649003289, 0.0005239737595642078, 0.000523973759571332, 0.0005239737595797443, 0.0005773932407917512, 0.0005773932407934662, 0.0005773932407945836, 0.0005790788253746123, 0.0005790788253774553, 0.0005790788253779831, 0.0006040034240449698, 0.000604003424045841, 0.0006040034240459377, 0.0006139296700493458, 0.00061392967005271, 0.00061392967005407, 0.0006336953465478912, 0.000633695346548282, 0.0006336953465503019, 0.0006348790199076738, 0.0006348790199103522, 0.0006348790199122382, 0.0006452115628213609, 0.0006452115628249428, 0.0006452115628287022, 0.0006583108630969198, 0.0006583108630977938, 0.0006583108631030258, 0.0006802125901020557, 0.0006802125901023802, 0.0006802125901092864, 0.0007200311129131547, 0.0007200311129157382, 0.0007200311129206584, 0.0007932872325677078, 0.0008076554699327818, 0.0008076554699409858, 0.0008076554699433273, 0.0008218351357600976, 0.0008218351357606448, 0.0008218351357671569, 0.0009040682164432678, 0.0009040682164456141, 0.0009040682164485006, 0.0009101648818636473, 0.0009101648818741396, 0.0009101648818818196, 0.0009545436306200294, 0.0009545436306268908, 0.0009545436306312901, 0.001116608586184538, 0.0011166085861889575, 0.0011166085861902028, 0.001224916907034844, 0.0012249169070348833, 0.0012249169070411472, 0.0013681663198938994, 0.0013681663199072208, 0.0013681663199158513, 0.0014250918814340463, 0.0014250918814431518, 0.0014250918814463257, 0.001517977935285033, 0.0015179779352933332, 0.0015179779352944818, 0.001521769855485114, 0.001521769855489261, 0.0015217698554902874, 0.0015499925409594095, 0.0015499925409598898, 0.0015499925409610817, 0.0015744566170081877, 0.0015744566170082241, 0.0015744566170109685, 0.0016030264549198375, 0.0016030264549219807, 0.0016030264549262445, 0.0016739002367449065, 0.00167390023675795, 0.0016739002367584183, 0.0016764283713183198, 0.0016764283713183751, 0.0016764283713199717, 0.001679265691424395, 0.0016792656914285016, 0.0016792656914316033, 0.001744383239019985, 0.001744383239027185, 0.0017443832390316317, 0.0018482705191982616, 0.0018482705192122333, 0.0018482705192153315, 0.00191279023267428, 0.0019127902326753633, 0.0019127902326829017, 0.00220492904019695, 0.0022049290401975953, 0.002204929040208505, 0.00245029757423239, 0.0024502975742393385, 0.0024502975742462865, 0.002481588450245397, 0.0025078657023787183, 0.0025078657023794634, 0.0025078657023869535, 0.002703211730841763, 0.002983445658017808, 0.00298344565801923, 0.0029834456580193486, 0.0031781597181588447}; return {std::move(x), std::move(w)}; } else if (m == 15) { // Xiao Gimbutas, 4 points, degree 15 std::vector x = {0.02484175292278384, 0.008848046489432548, 0.9372660613009632, 0.008848046489437518, 0.02904413928681676, 0.9372660613009608, 0.02904413928681213, 0.02484175292278018, 0.9372660613009554, 0.003073684779436487, 0.2792517608130759, 0.007826636168369566, 0.709847918239126, 0.003073684779447072, 0.007826636168374292, 0.2792517608130549, 0.7098479182391227, 0.007826636168374732, 0.8851662080559259, 0.01941685525440453, 0.09225347133747239, 0.01941685525440571, 0.003163465352201491, 0.09225347133747905, 0.003163465352203353, 0.8851662080559214, 0.09225347133747011, 0.02062270623222065, 0.9433792085073393, 0.01608597865131358, 0.943379208507337, 0.01991210660912664, 0.01608597865131474, 0.01991210660912658, 0.02062270623222047, 0.01608597865131562, 0.1095755475481473, 0.01464021932495235, 0.8630127527658966, 0.01277148036100595, 0.1095755475481476, 0.8630127527658954, 0.01464021932495353, 0.012771480361006, 0.8630127527659015, 0.01034990660067171, 0.7426915834872547, 0.01167275911784623, 0.2352857507942327, 0.01034990660067426, 0.0116727591178434, 0.7426915834872502, 0.2352857507942241, 0.01167275911784928, 0.0133390399676393, 0.3718583817617002, 0.6057046365583734, 0.3718583817617083, 0.009097941712290891, 0.6057046365583626, 0.009097941712294314, 0.01333903996763977, 0.6057046365583639, 0.5249771861414798, 0.01367226401253988, 0.01029208189692955, 0.01367226401254362, 0.4510584679490587, 0.01029208189693136, 0.4510584679490486, 0.5249771861414734, 0.01029208189693373, 0.0745911424654928, 0.09342069694332726, 0.004110311725985602, 0.8278778488651856, 0.07459114246549663, 0.004110311725986349, 0.0934206969433283, 0.8278778488651842, 0.004110311725989205, 0.0122514981411758, 0.11081079369917, 0.01880574820318091, 0.1108107936991686, 0.8581319599564727, 0.01880574820318023, 0.85813195995647, 0.01225149814117781, 0.01880574820318224, 0.01511843542458239, 0.04765944606068975, 0.0750995589695265, 0.04765944606069021, 0.8621225595452037, 0.07509955896952418, 0.8621225595452005, 0.01511843542458308, 0.0750995589695319, 0.1361581213833478, 0.001922023464433764, 0.2260822299666647, 0.6358376251855543, 0.13615812138335, 0.2260822299666622, 0.001922023464443689, 0.6358376251855318, 0.2260822299666766, 0.01376037881840447, 0.7415160297079681, 0.2282126571617323, 0.741516029707964, 0.01651093431189725, 0.2282126571617314, 0.01651093431189804, 0.01376037881840719, 0.2282126571617405, 0.5287234935734707, 0.3829175522239497, 0.08835752618992757, 1.428012658284353e-06, 0.5287234935734723, 0.08835752618992139, 0.382917552223954, 1.428012662889333e-06, 0.0883575261899238, 0.8603078190745503, 0.1006669391238628, 0.02182336879483741, 0.1006669391238637, 0.01720187300674862, 0.02182336879483792, 0.01720187300674926, 0.8603078190745502, 0.0218233687948384, 0.213749749548486, 0.003009152671000964, 0.6883673506451037, 0.003009152671001727, 0.09487374713541114, 0.6883673506451042, 0.09487374713541051, 0.213749749548485, 0.6883673506450955, 0.01776885891901024, 0.5596471440071771, 0.4090155222479943, 0.5596471440071775, 0.01356847482581952, 0.4090155222479928, 0.01356847482581988, 0.01776885891900956, 0.4090155222479974, 0.5561861612025368, 0.07185958939197906, 0.3631251266370949, 0.07185958939199275, 0.008829122768390513, 0.3631251266370967, 0.008829122768396735, 0.556186161202529, 0.3631251266370916, 0.02153556644467808, 0.01948609324379375, 0.7368319066698201, 0.01948609324379522, 0.2221464336417116, 0.7368319066698122, 0.2221464336417159, 0.02153556644468099, 0.736831906669808, 0.7706018404420292, 0.01488093241522564, 0.1457507819888409, 0.01488093241522547, 0.06876644515390219, 0.1457507819888461, 0.06876644515390468, 0.7706018404420206, 0.1457507819888496, 0.2632140692833737, 0.5416447947584936, 0.1940096424521396, 0.5416447947584961, 0.001131493505992412, 0.1940096424521405, 0.001131493505999361, 0.2632140692833789, 0.1940096424521402, 0.5866866785337993, 0.3700027679954775, 0.01920458791743641, 0.02410596555328862, 0.5866866785337975, 0.01920458791743763, 0.3700027679954874, 0.02410596555328817, 0.01920458791743828, 0.1007245253786872, 0.007839833208067415, 0.681257697378422, 0.007839833208073724, 0.2101779440348198, 0.6812576973784202, 0.2101779440348183, 0.1007245253786925, 0.6812576973784144, 0.2678058473170444, 0.009653727838177417, 0.3203870289323185, 0.009653727838177781, 0.4021533959124907, 0.3203870289323026, 0.4021533959124549, 0.2678058473170257, 0.3203870289323365, 0.01871950766556472, 0.07877407681846821, 0.8174647095402787, 0.07877407681846901, 0.08504170597569094, 0.8174647095402758, 0.08504170597569295, 0.01871950766556529, 0.8174647095402776, 0.2289622580755599, 0.7059180690514001, 0.01703231872933662, 0.7059180690513981, 0.04808735414370217, 0.01703231872933689, 0.04808735414368957, 0.2289622580755674, 0.01703231872933699, 0.01343952012215375, 0.188978322150464, 0.07671766832162848, 0.1889783221504602, 0.7208644894057589, 0.07671766832162524, 0.7208644894057502, 0.01343952012215593, 0.07671766832162946, 0.1856695714205305, 0.00940743474102812, 0.498715106027506, 0.3062078878109278, 0.1856695714205285, 0.4987151060275142, 0.009407434741032658, 0.306207887810909, 0.4987151060275297, 0.07134313304985408, 0.7168988880207371, 0.01586351574919136, 0.7168988880207331, 0.1958944631802114, 0.01586351574919187, 0.1958944631802093, 0.07134313304985869, 0.01586351574919585, 0.6081230921397938, 0.01132540328384632, 0.2960917880883974, 0.01132540328384961, 0.08445971648796122, 0.2960917880884005, 0.08445971648796277, 0.6081230921397877, 0.2960917880884, 0.7831901908047053, 0.08946628745362917, 0.1093980943900156, 0.01794542735164876, 0.7831901908046981, 0.1093980943900213, 0.08946628745363563, 0.01794542735165081, 0.1093980943900156, 0.5244117804550043, 0.2084577994081171, 0.01133447823052404, 0.2084577994081089, 0.255795941906351, 0.01133447823052516, 0.2557959419063432, 0.5244117804550297, 0.01133447823052761, 0.01588261437502311, 0.3863456880386881, 0.5325804036187812, 0.06519129396751876, 0.0158826143750247, 0.5325804036187707, 0.3863456880386817, 0.0651912939675148, 0.5325804036187769, 0.03236911071658531, 0.6667349326998179, 0.2320665678040404, 0.06882938877957372, 0.03236911071658261, 0.2320665678040363, 0.6667349326998023, 0.06882938877957215, 0.2320665678040363, 0.2154208621405496, 0.01783785666517354, 0.07794412055941285, 0.01783785666517584, 0.6887971606348672, 0.07794412055941696, 0.6887971606348626, 0.2154208621405385, 0.07794412055942193, 0.4031765079784496, 0.01499093171087654, 0.5021066311640049, 0.01499093171087712, 0.07972592914667045, 0.5021066311640021, 0.07972592914666891, 0.4031765079784464, 0.5021066311640048, 0.1848636811678036, 0.6499516355495718, 0.0182999648331871, 0.6499516355495523, 0.1468847184494371, 0.01829996483318704, 0.1468847184494266, 0.184863681167814, 0.01829996483318721, 0.1804453441648688, 0.451820102412822, 0.3540241023636928, 0.01371045105861957, 0.1804453441648663, 0.3540241023636804, 0.4518201024128161, 0.01371045105861805, 0.3540241023636947, 0.08105206237388583, 0.3766532156247404, 0.01518945086644218, 0.5271052711349223, 0.08105206237388458, 0.01518945086644177, 0.3766532156247387, 0.5271052711349294, 0.01518945086644269, 0.331408497762104, 0.1126435977537889, 0.0134868064234796, 0.5424610980606107, 0.3314084977621181, 0.01348680642347998, 0.1126435977537936, 0.5424610980606109, 0.01348680642348059, 0.5607935139776807, 0.01909773271486895, 0.06348929065765115, 0.01909773271486825, 0.3566194626497967, 0.06348929065765373, 0.3566194626497901, 0.5607935139776836, 0.0634892906576574, 0.07227368768344591, 0.2738988990502447, 0.6053416400059596, 0.04848577326034448, 0.07227368768344192, 0.6053416400059548, 0.2738988990502546, 0.04848577326034462, 0.6053416400059433, 0.3706144545187708, 0.01602307963396246, 0.1897016601820003, 0.01602307963396413, 0.4236608056652554, 0.1897016601819962, 0.4236608056652498, 0.3706144545187751, 0.1897016601820099, 0.07313934036770678, 0.06778253195192757, 0.7047038738820789, 0.1543742537982787, 0.0731393403677088, 0.7047038738820692, 0.06778253195194402, 0.1543742537982604, 0.7047038738820758, 0.2255550687888375, 0.3828261323216449, 0.01264429188566553, 0.3828261323216364, 0.3789745070038535, 0.01264429188566583, 0.3789745070038492, 0.2255550687888252, 0.01264429188566767, 0.01610664762850615, 0.1955976722408662, 0.504888949609758, 0.1955976722408699, 0.2834067305208563, 0.5048889496097685, 0.2834067305208526, 0.01610664762850647, 0.5048889496097625, 0.07394396840975409, 0.08972209215481677, 0.06088720165051376, 0.7754467377849116, 0.07394396840975978, 0.06088720165051331, 0.08972209215481254, 0.7754467377849096, 0.06088720165051947, 0.02198550415554825, 0.4522698873067332, 0.3656528451019294, 0.4522698873067705, 0.1600917634357737, 0.3656528451019036, 0.1600917634358059, 0.02198550415555332, 0.3656528451018906, 0.6425340162574237, 0.03023340240085394, 0.1777342490770398, 0.1494983322647011, 0.6425340162574126, 0.1777342490770339, 0.03023340240085156, 0.1494983322646933, 0.1777342490770401, 0.2982386203137134, 0.3492984375384283, 0.3282766894848435, 0.02418625266301134, 0.2982386203137263, 0.3282766894848561, 0.3492984375384166, 0.02418625266301722, 0.3282766894848335, 0.02492578585848872, 0.5613484323473249, 0.1898351044830521, 0.2238906773111161, 0.02492578585848644, 0.1898351044830608, 0.5613484323473145, 0.22389067731113, 0.1898351044830645, 0.1769461529053036, 0.05090872345953187, 0.6068946490861404, 0.1652504745490214, 0.1769461529053149, 0.6068946490861201, 0.05090872345953583, 0.1652504745490102, 0.6068946490861318, 0.267326785868413, 0.09253312310838845, 0.162728234374953, 0.09253312310841946, 0.4774118566482484, 0.1627282343749322, 0.4774118566482272, 0.2673267858684251, 0.162728234374927, 0.6324086041104882, 0.194821777558372, 0.07976498347271095, 0.09300463485842678, 0.6324086041104942, 0.0797649834727117, 0.1948217775583615, 0.09300463485842231, 0.07976498347272819, 0.1286081121536503, 0.06206882668877994, 0.5069361346327252, 0.3023869265248393, 0.1286081121536543, 0.5069361346327149, 0.06206882668879442, 0.3023869265248427, 0.5069361346327274, 0.0670060501118351, 0.07446918750056565, 0.362874044201665, 0.4956507181859352, 0.06700605011182939, 0.3628740442016639, 0.07446918750057789, 0.4956507181859199, 0.3628740442016637, 0.04935190074328522, 0.3139246227522204, 0.1497252367121568, 0.3139246227522261, 0.4869982397923315, 0.1497252367121615, 0.4869982397923459, 0.04935190074327669, 0.149725236712158, 0.1083529025389752, 0.08656298623704123, 0.1758982241285937, 0.629185887095386, 0.1083529025389925, 0.1758982241285794, 0.08656298623703901, 0.629185887095383, 0.1758982241285948, 0.4947915054658847, 0.3668061114674768, 0.0797762381765776, 0.05862614489007139, 0.4947915054658749, 0.07977623817658046, 0.3668061114674683, 0.05862614489007745, 0.07977623817658072, 0.6107564898848346, 0.09155687274905353, 0.08682921283620239, 0.2108574245299135, 0.6107564898848316, 0.0868292128362, 0.09155687274905454, 0.2108574245299094, 0.08682921283620657, 0.27189840912706, 0.2718984091270191, 0.1843047726189553, 0.08364528610473303, 0.4598959913496589, 0.2836943220226789, 0.4598959913496499, 0.1727644005229201, 0.2836943220226703, 0.1727644005229245, 0.0836452861047611, 0.2836943220226696, 0.220307180915872, 0.2203071809158613, 0.3390784572525623, 0.4498492745599813, 0.1402964159526019, 0.06810558755187382, 0.3417487219355564, 0.4498492745599653, 0.0681055875518764, 0.1402964159526053, 0.3417487219355464, 0.06810558755187403, 0.4796245561760771, 0.06988987460087778, 0.2572931400794461, 0.1931924291436032, 0.4796245561760668, 0.2572931400794509, 0.06988987460089365, 0.1931924291436036, 0.2572931400794577, 0.4819852013641325, 0.2605590256904803, 0.06519355736441289, 0.260559025690468, 0.1922622155809665, 0.06519355736441458, 0.1922622155809799, 0.481985201364128, 0.06519355736441629, 0.09109001795840514, 0.343030573977829, 0.2354338687541656, 0.3304455393096081, 0.09109001795841783, 0.2354338687541547, 0.343030573977809, 0.3304455393095925, 0.2354338687541694, 0.08624240167701186, 0.1525520261883117, 0.4323311285083857, 0.3288744436262873, 0.0862424016770105, 0.4323311285083789, 0.1525520261883389, 0.3288744436262735, 0.4323311285083771, 0.3337847727363109, 0.3372700741462638, 0.1536568898623008, 0.3372700741462386, 0.1752882632551174, 0.1536568898623003, 0.1752882632551262, 0.333784772736308, 0.1536568898623202, 0.3113177461109946, 0.3113177461109871, 0.06604676166701309, 0.2898527003164425, 0.2359060680311884, 0.3891009454304513, 0.2359060680311902, 0.08514028622192746, 0.3891009454304601, 0.0851402862219274, 0.2898527003164147, 0.38910094543047, 0.1915586123296988, 0.4738597830753307, 0.1624688668332693, 0.4738597830753175, 0.1721127377617093, 0.1624688668332666, 0.1721127377617219, 0.1915586123296906, 0.1624688668332857, 0.343066772448071, 0.1725513660667974, 0.2910280425505992, 0.1933538189344859, 0.3430667724481031, 0.2910280425505916, 0.1725513660668472, 0.1933538189345398, 0.2910280425506466, 0.1627284320161738, 0.1627284320161655, 0.5118147039514781}; std::vector w = {5.0949499461256536e-05, 5.094949946129215e-05, 5.0949499461363764e-05, 8.091578025333847e-05, 8.09157802534108e-05, 8.091578025341743e-05, 9.003858468151016e-05, 9.003858468155147e-05, 9.003858468155359e-05, 0.00011096788435521842, 0.00011096788435523033, 0.00011096788435523135, 0.00014301129642297394, 0.0001430112964229811, 0.00014301129642299053, 0.0001435686140277277, 0.00014356861402773594, 0.00014356861402778517, 0.00017047619231215117, 0.00017047619231217833, 0.00017047619231222533, 0.00017795546384389533, 0.00017795546384394634, 0.00017795546384398616, 0.00018791251008007368, 0.00018791251008008316, 0.00018791251008011434, 0.00018853254351190317, 0.00018853254351192117, 0.0001885325435119355, 0.000203420107255822, 0.00020342010725582285, 0.00020342010725585117, 0.00023831202538122532, 0.000238312025381286, 0.00023831202538146515, 0.00024702490709429967, 0.0002470249070943653, 0.00024702490709436765, 0.00025848719013847165, 0.00025848719013854435, 0.00025848719013861867, 0.000265583230932468, 0.00026558323093246863, 0.0002655832309324773, 0.00029768749691803264, 0.0002976874969180593, 0.000297687496918183, 0.0002989438206765175, 0.0002989438206765232, 0.0002989438206765322, 0.0003205499531253637, 0.0003205499531254577, 0.0003205499531255287, 0.0003351988396668535, 0.00033519883966689985, 0.00033519883966691964, 0.000357511187619173, 0.000357511187619211, 0.00035751118761923035, 0.0003766593641388355, 0.00037665936413884397, 0.00037665936413898253, 0.0004170560045706985, 0.00041705600457073996, 0.00041705600457075487, 0.0004206908093603285, 0.0004206908093604563, 0.00042069080936051436, 0.0004403404439190608, 0.0004403404439191172, 0.00044034044391935267, 0.0004510176089945963, 0.00045101760899459884, 0.0004510176089946125, 0.00048092536064853767, 0.0004809253606485387, 0.0004809253606485603, 0.0004921683513968802, 0.000492168351396902, 0.0004921683513969398, 0.0004972651138408006, 0.0004972651138408805, 0.0004972651138410044, 0.0005046209614997817, 0.0005046209614998177, 0.0005046209614999028, 0.0005053606662589742, 0.0005053606662590495, 0.0005053606662590833, 0.000507488410074817, 0.000507488410074818, 0.0005074884100748625, 0.0005296283626829228, 0.0005296283626829545, 0.0005296283626830274, 0.0005961391122951584, 0.0005961391122951768, 0.0005961391122952683, 0.0006063199394915311, 0.0006063199394916362, 0.0006063199394917882, 0.0006305106887630419, 0.0006305106887630918, 0.0006305106887631449, 0.0006490437338464095, 0.00064904373384643, 0.0006490437338465401, 0.0006784672244327731, 0.0006784672244328733, 0.0006784672244328974, 0.0007018738744954993, 0.0007018738744955992, 0.0007018738744956027, 0.0007044998612670034, 0.0007044998612670082, 0.0007044998612670204, 0.0007123210856255668, 0.0007123210856256313, 0.0007123210856256491, 0.0007180840505148808, 0.0007180840505148981, 0.0007180840505149077, 0.0007320095837554124, 0.000732009583755433, 0.0007320095837555596, 0.0007487529433902918, 0.0007487529433903294, 0.0007487529433904035, 0.0007633124179686057, 0.0007633124179687115, 0.0007633124179687585, 0.0007665117920190594, 0.0007665117920191146, 0.0007665117920191253, 0.000782684926528277, 0.0007826849265283078, 0.0007826849265283831, 0.0008216462765614071, 0.0008216462765614102, 0.0008216462765614202, 0.0008401904995806664, 0.0008401904995807659, 0.000840190499580874, 0.0008649691231126786, 0.0008649691231126842, 0.0008649691231126886, 0.0009771082729624124, 0.0009771082729625377, 0.0009771082729625865, 0.001003226140444279, 0.0010032261404442897, 0.001003226140444467, 0.0011107068782117186, 0.0011107068782118005, 0.001110706878211849, 0.0011541194419159395, 0.0011541194419163337, 0.0011541194419164846, 0.0011888869781639686, 0.0011888869781640197, 0.0011888869781641177, 0.0012039315081215961, 0.0012039315081217672, 0.0012039315081217863, 0.0012677548100906834, 0.0012677548100907092, 0.0012677548100908191, 0.0013075346711990234, 0.001307534671199086, 0.0013075346711991051, 0.0013386099794468343, 0.0013386099794469022, 0.0013386099794470629, 0.0013454689764230332, 0.0013454689764231366, 0.0013454689764231468, 0.001398358195255936, 0.0013983581952560142, 0.0013983581952561012, 0.001511722175784935, 0.001536829401278278, 0.0015368294012785917, 0.0015368294012786117, 0.001568496765944744, 0.0016371705077032532, 0.0016371705077033274, 0.0016371705077033551, 0.0016680098648530085, 0.0016680098648530152, 0.0016680098648534517, 0.0016801415184265068, 0.0016801415184265085, 0.0016801415184265866, 0.0016912148002698566, 0.0016912148002698867, 0.0016912148002702135, 0.0018093524420684734, 0.0018093524420685033, 0.001809352442068565, 0.0018270943485695949, 0.0018270943485701834, 0.0018270943485707415, 0.0018747167751995899, 0.0018892190654910483, 0.0018892190654914516, 0.0018892190654915483, 0.0019096117089971417, 0.0019096117089973984, 0.0019096117089977185, 0.002049458570185687, 0.00204945857018569, 0.002049458570187277, 0.0024074895531075533}; return {std::move(x), std::move(w)}; } else throw std::runtime_error("Xiao-Gimbutas not implemented for this order."); } else { throw std::runtime_error( "Xiao-Gimbutas is only implemented for triangles."); } } //----------------------------------------------------------------------------- template std::array, 2> make_macroedge_quadrature(quadrature::type rule, cell::type celltype, int m) { auto standard_q = quadrature::make_quadrature(rule, celltype, polyset::type::standard, m); if (m == 0) { return standard_q; } switch (celltype) { case cell::type::interval: { const std::size_t npts = standard_q[0].size(); std::vector x(npts * 2); std::vector w(npts * 2); for (std::size_t i = 0; i < npts; ++i) { x[i] = 0.5 * standard_q[0][i]; x[npts + i] = 0.5 + 0.5 * standard_q[0][i]; w[i] = 0.5 * standard_q[1][i]; w[npts + i] = 0.5 * standard_q[1][i]; } return {std::move(x), std::move(w)}; } case cell::type::triangle: { const std::size_t npts = standard_q[0].size() / 2; std::vector x(npts * 8); std::vector w(npts * 4); for (std::size_t i = 0; i < npts; ++i) { x[2 * i] = 0.5 * standard_q[0][2 * i]; x[2 * i + 1] = 0.5 * standard_q[0][2 * i + 1]; x[2 * (npts + i)] = 0.5 + 0.5 * standard_q[0][2 * i]; x[2 * (npts + i) + 1] = 0.5 * standard_q[0][2 * i + 1]; x[2 * (2 * npts + i)] = 0.5 * standard_q[0][2 * i]; x[2 * (2 * npts + i) + 1] = 0.5 + 0.5 * standard_q[0][2 * i + 1]; x[2 * (3 * npts + i)] = 0.5 - 0.5 * standard_q[0][2 * i]; x[2 * (3 * npts + i) + 1] = 0.5 - 0.5 * standard_q[0][2 * i + 1]; w[i] = 0.25 * standard_q[1][i]; w[npts + i] = 0.25 * standard_q[1][i]; w[2 * npts + i] = 0.25 * standard_q[1][i]; w[3 * npts + i] = 0.25 * standard_q[1][i]; } return {std::move(x), std::move(w)}; } case cell::type::tetrahedron: { const std::size_t npts = standard_q[0].size() / 3; std::vector x(npts * 24); std::vector w(npts * 8); for (std::size_t i = 0; i < npts; ++i) { x[3 * i] = 0.5 * standard_q[0][3 * i]; x[3 * i + 1] = 0.5 * standard_q[0][3 * i + 1]; x[3 * i + 2] = 0.5 * standard_q[0][3 * i + 2]; x[3 * (i + npts)] = 1.0 - 0.5 * standard_q[0][3 * i] - 0.5 * standard_q[0][3 * i + 1] - 0.5 * standard_q[0][3 * i + 2]; x[3 * (i + npts) + 1] = 0.5 * standard_q[0][3 * i]; x[3 * (i + npts) + 2] = 0.5 * standard_q[0][3 * i + 2]; x[3 * (i + 2 * npts)] = 0.5 * standard_q[0][3 * i]; x[3 * (i + 2 * npts) + 1] = 1.0 - 0.5 * standard_q[0][3 * i] - 0.5 * standard_q[0][3 * i + 1] - 0.5 * standard_q[0][3 * i + 2]; x[3 * (i + 2 * npts) + 2] = 0.5 * standard_q[0][3 * i + 1]; x[3 * (i + 3 * npts)] = 0.5 * standard_q[0][3 * i + 1]; x[3 * (i + 3 * npts) + 1] = 0.5 * standard_q[0][3 * i]; x[3 * (i + 3 * npts) + 2] = 1.0 - 0.5 * standard_q[0][3 * i] - 0.5 * standard_q[0][3 * i + 1] - 0.5 * standard_q[0][3 * i + 2]; x[3 * (i + 4 * npts)] = 0.5 - 0.5 * standard_q[0][3 * i + 1] - 0.5 * standard_q[0][3 * i + 2]; x[3 * (i + 4 * npts) + 1] = 0.5 * standard_q[0][3 * i + 2]; x[3 * (i + 4 * npts) + 2] = 0.5 - 0.5 * standard_q[0][3 * i] - 0.5 * standard_q[0][3 * i + 2]; x[3 * (i + 5 * npts)] = 0.5 * standard_q[0][3 * i] + 0.5 * standard_q[0][3 * i + 1] + 0.5 * standard_q[0][3 * i + 2]; x[3 * (i + 5 * npts) + 1] = 0.5 - 0.5 * standard_q[0][3 * i + 1] - 0.5 * standard_q[0][3 * i + 2]; x[3 * (i + 5 * npts) + 2] = 0.5 * standard_q[0][3 * i + 1]; x[3 * (i + 6 * npts)] = 0.5 - 0.5 * standard_q[0][3 * i + 1] - 0.5 * standard_q[0][3 * i + 2]; x[3 * (i + 6 * npts) + 1] = 0.5 * standard_q[0][3 * i] + 0.5 * standard_q[0][3 * i + 1] + 0.5 * standard_q[0][3 * i + 2]; x[3 * (i + 6 * npts) + 2] = 0.5 - 0.5 * standard_q[0][3 * i] - 0.5 * standard_q[0][3 * i + 1]; x[3 * (i + 7 * npts)] = 0.5 * standard_q[0][3 * i + 2]; x[3 * (i + 7 * npts) + 1] = 0.5 - 0.5 * standard_q[0][3 * i + 1] - 0.5 * standard_q[0][3 * i + 2]; x[3 * (i + 7 * npts) + 2] = 0.5 * standard_q[0][3 * i] + 0.5 * standard_q[0][3 * i + 1] + 0.5 * standard_q[0][3 * i + 2]; w[i] = 0.125 * standard_q[1][i]; w[npts + i] = 0.125 * standard_q[1][i]; w[2 * npts + i] = 0.125 * standard_q[1][i]; w[3 * npts + i] = 0.125 * standard_q[1][i]; w[4 * npts + i] = 0.125 * standard_q[1][i]; w[5 * npts + i] = 0.125 * standard_q[1][i]; w[6 * npts + i] = 0.125 * standard_q[1][i]; w[7 * npts + i] = 0.125 * standard_q[1][i]; } return {std::move(x), std::move(w)}; } case cell::type::quadrilateral: { const std::size_t npts = standard_q[0].size() / 2; std::vector x(npts * 8); std::vector w(npts * 4); for (std::size_t i = 0; i < npts; ++i) { x[2 * i] = 0.5 * standard_q[0][2 * i]; x[2 * i + 1] = 0.5 * standard_q[0][2 * i + 1]; x[2 * (npts + i)] = 0.5 + 0.5 * standard_q[0][2 * i]; x[2 * (npts + i) + 1] = 0.5 * standard_q[0][2 * i + 1]; x[2 * (2 * npts + i)] = 0.5 * standard_q[0][2 * i]; x[2 * (2 * npts + i) + 1] = 0.5 + 0.5 * standard_q[0][2 * i + 1]; x[2 * (3 * npts + i)] = 0.5 + 0.5 * standard_q[0][2 * i]; x[2 * (3 * npts + i) + 1] = 0.5 + 0.5 * standard_q[0][2 * i + 1]; w[i] = 0.25 * standard_q[1][i]; w[npts + i] = 0.25 * standard_q[1][i]; w[2 * npts + i] = 0.25 * standard_q[1][i]; w[3 * npts + i] = 0.25 * standard_q[1][i]; } return {std::move(x), std::move(w)}; } case cell::type::hexahedron: { const std::size_t npts = standard_q[0].size() / 3; std::vector x(npts * 24); std::vector w(npts * 8); for (std::size_t i = 0; i < npts; ++i) { x[3 * i] = 0.5 * standard_q[0][3 * i]; x[3 * i + 1] = 0.5 * standard_q[0][3 * i + 1]; x[3 * i + 2] = 0.5 * standard_q[0][3 * i + 2]; x[3 * (npts + i)] = 0.5 + 0.5 * standard_q[0][3 * i]; x[3 * (npts + i) + 1] = 0.5 * standard_q[0][3 * i + 1]; x[3 * (npts + i) + 2] = 0.5 * standard_q[0][3 * i + 2]; x[3 * (2 * npts + i)] = 0.5 * standard_q[0][3 * i]; x[3 * (2 * npts + i) + 1] = 0.5 + 0.5 * standard_q[0][3 * i + 1]; x[3 * (2 * npts + i) + 2] = 0.5 * standard_q[0][3 * i + 2]; x[3 * (3 * npts + i)] = 0.5 + 0.5 * standard_q[0][3 * i]; x[3 * (3 * npts + i) + 1] = 0.5 + 0.5 * standard_q[0][3 * i + 1]; x[3 * (3 * npts + i) + 2] = 0.5 * standard_q[0][3 * i + 2]; x[3 * (4 * npts + i)] = 0.5 * standard_q[0][3 * i]; x[3 * (4 * npts + i) + 1] = 0.5 * standard_q[0][3 * i + 1]; x[3 * (4 * npts + i) + 2] = 0.5 + 0.5 * standard_q[0][3 * i + 2]; x[3 * (5 * npts + i)] = 0.5 + 0.5 * standard_q[0][3 * i]; x[3 * (5 * npts + i) + 1] = 0.5 * standard_q[0][3 * i + 1]; x[3 * (5 * npts + i) + 2] = 0.5 + 0.5 * standard_q[0][3 * i + 2]; x[3 * (6 * npts + i)] = 0.5 * standard_q[0][3 * i]; x[3 * (6 * npts + i) + 1] = 0.5 + 0.5 * standard_q[0][3 * i + 1]; x[3 * (6 * npts + i) + 2] = 0.5 + 0.5 * standard_q[0][3 * i + 2]; x[3 * (7 * npts + i)] = 0.5 + 0.5 * standard_q[0][3 * i]; x[3 * (7 * npts + i) + 1] = 0.5 + 0.5 * standard_q[0][3 * i + 1]; x[3 * (7 * npts + i) + 2] = 0.5 + 0.5 * standard_q[0][3 * i + 2]; w[i] = 0.125 * standard_q[1][i]; w[npts + i] = 0.125 * standard_q[1][i]; w[2 * npts + i] = 0.125 * standard_q[1][i]; w[3 * npts + i] = 0.125 * standard_q[1][i]; w[4 * npts + i] = 0.125 * standard_q[1][i]; w[5 * npts + i] = 0.125 * standard_q[1][i]; w[6 * npts + i] = 0.125 * standard_q[1][i]; w[7 * npts + i] = 0.125 * standard_q[1][i]; } return {std::move(x), std::move(w)}; } default: throw std::runtime_error("Macro quadrature not supported on this cell."); } } //----------------------------------------------------------------------------- } // namespace //----------------------------------------------------------------------------- quadrature::type quadrature::get_default_rule(cell::type celltype, int m) { if (celltype == cell::type::triangle) { if (m <= 1) return type::zienkiewicz_taylor; else if (m <= 6) return type::strang_fix; else if (m <= 30) return type::xiao_gimbutas; else return type::gauss_jacobi; } else if (celltype == cell::type::tetrahedron) { if (m <= 3) return type::zienkiewicz_taylor; else if (m <= 8) return type::keast; else if (m <= 15) return type::xiao_gimbutas; else return type::gauss_jacobi; } else return type::gauss_jacobi; } //----------------------------------------------------------------------------- template std::array, 2> quadrature::make_quadrature(quadrature::type rule, cell::type celltype, polyset::type polytype, int m) { switch (polytype) { case polyset::type::standard: switch (rule) { case quadrature::type::Default: return make_quadrature(get_default_rule(celltype, m), celltype, polytype, m); case quadrature::type::gauss_jacobi: return make_gauss_jacobi_quadrature(celltype, m); case quadrature::type::gll: return make_gll_quadrature(celltype, m); case quadrature::type::xiao_gimbutas: return make_xiao_gimbutas_quadrature(celltype, m); case quadrature::type::zienkiewicz_taylor: return make_zienkiewicz_taylor_quadrature(celltype, m); case quadrature::type::keast: return make_keast_quadrature(celltype, m); case quadrature::type::strang_fix: return make_strang_fix_quadrature(celltype, m); default: throw std::runtime_error("Unknown quadrature rule"); } case polyset::type::macroedge: return make_macroedge_quadrature(rule, celltype, m); default: throw std::runtime_error("Unsupported polyset type"); } } //----------------------------------------------------------------------------- template std::vector quadrature::get_gl_points(int m) { std::vector pts = compute_gauss_jacobi_points(0, m); std::ranges::transform(pts, pts.begin(), [](auto x) { return 0.5 + 0.5 * x; }); return pts; } //----------------------------------------------------------------------------- template std::vector quadrature::get_gll_points(int m) { return make_gll_line(m)[0]; } //----------------------------------------------------------------------------- template std::array, 2> quadrature::make_quadrature(quadrature::type, cell::type, polyset::type, int); template std::array, 2> quadrature::make_quadrature(quadrature::type, cell::type, polyset::type, int); template std::vector quadrature::get_gl_points(int); template std::vector quadrature::get_gl_points(int); template std::vector quadrature::get_gll_points(int); template std::vector quadrature::get_gll_points(int); //----------------------------------------------------------------------------- fenics-basix-0.9.0/cpp/basix/quadrature.h000066400000000000000000000035241470517546000203260ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson // FEniCS Project // SPDX-License-Identifier: MIT #pragma once #include "cell.h" #include "polyset.h" #include #include #include /// Quadrature rules namespace basix::quadrature { /// Quadrature type enum class type { Default = 0, gauss_jacobi = 1, gll = 2, xiao_gimbutas = 3, zienkiewicz_taylor = 20, keast = 21, strang_fix = 22, }; /// @brief Make a quadrature rule on a reference cell. /// @param[in] rule Type of quadrature rule (or use quadrature::Default). /// @param[in] celltype Cell type. /// @param[in] polytype Polyset type. /// @param[in] m Maximum degree of polynomial that this quadrature rule /// will integrate exactly. /// @return List of points and list of weights. The number of points /// arrays has shape `(num points, gdim)`. template std::array, 2> make_quadrature(const quadrature::type rule, cell::type celltype, polyset::type polytype, int m); /// @brief Get the default quadrature type for the given cell and order. /// @param[in] celltype Cell type. /// @param[in] m Maximum degree of polynomial that this quadrature rule /// will integrate exactly. /// @return Quadrature type that will be used by default. quadrature::type get_default_rule(cell::type celltype, int m); /// @brief Get Gauss-Lobatto-Legendre (GLL) points on the interval [0, /// 1]. /// @param[in] m Number of points. /// @return Array of GLL points. template std::vector get_gll_points(int m); /// @brief Get Gauss-Legendre (GL) points on the interval [0, 1]. /// @param[in] m Number of points /// @return Array of GL points. template std::vector get_gl_points(int m); } // namespace basix::quadrature fenics-basix-0.9.0/cpp/basix/sobolev-spaces.cpp000066400000000000000000000017061470517546000214310ustar00rootroot00000000000000// Copyright (c) 2022 Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #include "sobolev-spaces.h" basix::sobolev::space basix::sobolev::space_intersection(space space1, space space2) { if (space1 == space2) return space1; if (space1 == space::H1) { if (space2 == space::H2 or space2 == space::H3 or space2 == space::HInf) return space1; } else if (space1 == space::H2) { if (space2 == space::H1) return space2; else if (space2 == space::H3 or space2 == space::HInf) return space1; } else if (space1 == space::H3) { if (space2 == space::H1 or space2 == space::H2) return space2; else if (space2 == space::H3 or space2 == space::HInf) return space1; } else if (space1 == space::HInf) { if (space2 == space::H1 or space2 == space::H2 or space2 == space::H3) return space2; } return space::L2; } fenics-basix-0.9.0/cpp/basix/sobolev-spaces.h000066400000000000000000000010661470517546000210750ustar00rootroot00000000000000// Copyright (c) 2022 Matthew Scroggs // FEniCS Project // SPDX-License-Identifier: MIT #pragma once /// Information about Sobolev spaces namespace basix::sobolev { /// Sobolev space type enum class space { L2 = 0, H1 = 1, H2 = 2, H3 = 3, HInf = 8, HDiv = 10, HCurl = 11, HEin = 12, HDivDiv = 13, }; /// Get the intersection of two Sobolev spaces. /// @param[in] space1 First space /// @param[in] space2 Second space /// @return Intersection of the spaces space space_intersection(space space1, space space2); } // namespace basix::sobolev fenics-basix-0.9.0/cpp/basix/version.h.in000066400000000000000000000004571470517546000202450ustar00rootroot00000000000000// Copyright (c) 2020 Chris Richardson // FEniCS Project // SPDX-License-Identifier: MIT #pragma once #define BASIX_VERSION @PROJECT_VERSION@ #define BASIX_VERSION_MAJOR @PROJECT_VERSION_MAJOR@ #define BASIX_VERSION_MINOR @PROJECT_VERSION_MINOR@ #define BASIX_VERSION_PATCH @PROJECT_VERSION_PATCH@ fenics-basix-0.9.0/cpp/vcpkg.json000066400000000000000000000003051470517546000166710ustar00rootroot00000000000000{ "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json", "dependencies": [ "blas", "lapack", "vcpkg-cmake", "vcpkg-cmake-config" ] } fenics-basix-0.9.0/demo/000077500000000000000000000000001470517546000150305ustar00rootroot00000000000000fenics-basix-0.9.0/demo/cpp/000077500000000000000000000000001470517546000156125ustar00rootroot00000000000000fenics-basix-0.9.0/demo/cpp/conftest.py000066400000000000000000000003461470517546000200140ustar00rootroot00000000000000import sys def pytest_addoption(parser): parser.addoption( "--cmake-args", action="store", default=f"-DPython3_EXECUTABLE={sys.executable}", help="arguments to pass to cmake configure", ) fenics-basix-0.9.0/demo/cpp/demo_create_and_tabulate/000077500000000000000000000000001470517546000225645ustar00rootroot00000000000000fenics-basix-0.9.0/demo/cpp/demo_create_and_tabulate/CMakeLists.txt000066400000000000000000000021401470517546000253210ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.16) project(demo_create_and_tabulate LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) # Use Python for detecting Basix when installed using combined build find_package(Python3 COMPONENTS Interpreter) if (${Python3_FOUND}) execute_process( COMMAND ${Python3_EXECUTABLE} -c "import basix, os, sys; sys.stdout.write(os.path.dirname(basix.__file__))" OUTPUT_VARIABLE BASIX_PY_DIR RESULT_VARIABLE BASIX_PY_COMMAND_RESULT ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (BASIX_PY_DIR) message(STATUS "Adding ${BASIX_PY_DIR} to Basix search hints") endif() endif() find_package(Basix REQUIRED CONFIG HINTS ${BASIX_PY_DIR}) add_executable(${PROJECT_NAME} main.cpp) if (BASIX_PY_DIR AND IS_DIRECTORY ${BASIX_PY_DIR}/../fenics_basix.libs) set_target_properties(${PROJECT_NAME} PROPERTIES BUILD_RPATH ${BASIX_PY_DIR}/../fenics_basix.libs) set_target_properties(${PROJECT_NAME} PROPERTIES INSTALL_RPATH ${BASIX_PY_DIR}/../fenics_basix.libs) endif() target_link_libraries(${PROJECT_NAME} Basix::basix) fenics-basix-0.9.0/demo/cpp/demo_create_and_tabulate/main.cpp000066400000000000000000000040701470517546000242150ustar00rootroot00000000000000// ================================== // Creating and tabulating an element // ================================== // // This demo shows how Basix can be used to create an element // and tabulate the values of its basis functions at a set of // points. #include #include #include namespace stdex = MDSPAN_IMPL_STANDARD_NAMESPACE::MDSPAN_IMPL_PROPOSED_NAMESPACE; template using mdspan_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents>; using T = double; int main(int argc, char* argv[]) { // Create a degree 4 Lagrange element on a quadrilateral // For Lagrange elements, we use `basix::element::family::P`. auto family = basix::element::family::P; auto cell_type = basix::cell::type::quadrilateral; int k = 3; // For Lagrange elements, we must provide and extra argument: the Lagrange // variant. In this example, we use the equispaced variant: this will place // the degrees of freedom (DOFs) of the element in an equally spaced lattice. auto variant = basix::element::lagrange_variant::equispaced; // Create the lagrange element basix::FiniteElement lagrange = basix::create_element( family, cell_type, k, variant, basix::element::dpc_variant::unset, false); // Get the number of degrees of freedom for the element int dofs = lagrange.dim(); assert(dofs == (k + 1) * (k + 1)); // Create a set of points, and tabulate the basis functions // of the Lagrange element at these points. std::vector points = {0.0, 0.0, 0.1, 0.1, 0.2, 0.3, 0.3, 0.6, 0.4, 1.0}; auto [tab_data, shape] = lagrange.tabulate(0, points, {points.size() / 2, 2}); std::cout << "Tabulate data shape: [ "; for (auto s : shape) std::cout << s << " "; std::cout << "]" << std::endl; mdspan_t tab(tab_data.data(), shape); std::cout << "Tabulate data (0, 0, :, 0): [ "; for (std::size_t i = 0; i < tab.extent(2); ++i) std::cout << tab(0, 0, i, 0) << " "; std::cout << "]" << std::endl; return 0; } fenics-basix-0.9.0/demo/cpp/demo_dof_transformations/000077500000000000000000000000001470517546000226775ustar00rootroot00000000000000fenics-basix-0.9.0/demo/cpp/demo_dof_transformations/CMakeLists.txt000066400000000000000000000021401470517546000254340ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.16) project(demo_dof_transformations LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) # Use Python for detecting Basix when installed using combined build find_package(Python3 COMPONENTS Interpreter) if (${Python3_FOUND}) execute_process( COMMAND ${Python3_EXECUTABLE} -c "import basix, os, sys; sys.stdout.write(os.path.dirname(basix.__file__))" OUTPUT_VARIABLE BASIX_PY_DIR RESULT_VARIABLE BASIX_PY_COMMAND_RESULT ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (BASIX_PY_DIR) message(STATUS "Adding ${BASIX_PY_DIR} to Basix search hints") endif() endif() find_package(Basix REQUIRED CONFIG HINTS ${BASIX_PY_DIR}) add_executable(${PROJECT_NAME} main.cpp) if (BASIX_PY_DIR AND IS_DIRECTORY ${BASIX_PY_DIR}/../fenics_basix.libs) set_target_properties(${PROJECT_NAME} PROPERTIES BUILD_RPATH ${BASIX_PY_DIR}/../fenics_basix.libs) set_target_properties(${PROJECT_NAME} PROPERTIES INSTALL_RPATH ${BASIX_PY_DIR}/../fenics_basix.libs) endif() target_link_libraries(${PROJECT_NAME} Basix::basix) fenics-basix-0.9.0/demo/cpp/demo_dof_transformations/main.cpp000066400000000000000000000216201470517546000243300ustar00rootroot00000000000000// ==================================== // DOF permutations and transformations // ==================================== // When using high degree finite elements on general meshes, adjustments // may need to be made to correct for differences in the orientation of // mesh entities on the mesh and on the reference cell. For example, in // a degree 4 Lagrange element on a triangle, there are 3 degrees of // freedom (DOFs) associated with each edge. If two neighbouring cells in // a mesh disagree on the direction of the edge, they could put an // incorrectly combine the local basis functions to give the wrong global // basis function. // This issue and the use of permutations and transformations to correct // it is discussed in detail in `Construction of arbitrary order finite // element degree-of-freedom maps on polygonal and polyhedral cell // meshes (Scroggs, Dokken, Richardons, Wells, // 2021) `_. // Functions to permute and transform high degree elements are // provided by Basix. In this demo, we show how these can be used from C++. #include #include #include #include #include using T = double; static const std::map type_to_name = {{basix::cell::type::point, "point"}, {basix::cell::type::interval, "interval"}, {basix::cell::type::triangle, "triangle"}, {basix::cell::type::tetrahedron, "tetrahedron"}, {basix::cell::type::quadrilateral, "quadrilateral"}, {basix::cell::type::pyramid, "pyramid"}, {basix::cell::type::prism, "prism"}, {basix::cell::type::hexahedron, "hexahedron"}}; int main(int argc, char* argv[]) { // Degree 5 Lagrange element // ========================= { std::cout << "Degree 5 Lagrange element" << std::endl; // Create a degree 5 Lagrange element on a triangle auto family = basix::element::family::P; auto cell_type = basix::cell::type::triangle; int degree = 5; auto variant = basix::element::lagrange_variant::equispaced; // Create the lagrange element basix::FiniteElement lagrange = basix::create_element(family, cell_type, degree, variant, basix::element::dpc_variant::unset, false); // Print bools as true/false instead of 0/1 std::cout << std::boolalpha; // The value of `dof_transformations_are_identity` is `false`: this tells // us that permutations or transformations are needed for this element. std::cout << "Dof transformations are identity: " << lagrange.dof_transformations_are_identity() << std::endl; // The value of `dof_transformations_are_permutations` is `true`: this // tells us that for this element, all the corrections we need to apply // permutations. This is the simpler case, and means we make the // orientation corrections by applying permutations when creating the // DOF map. std::cout << "Dof transformations are permutations: " << lagrange.dof_transformations_are_permutations() << std::endl; // We can apply permutations by using the matrices returned by the // method `base_transformations`. This method will return one matrix // for each edge of the cell (for 2D and 3D cells), and two matrices // for each face of the cell (for 3D cells). These describe the effect // of reversing the edge or reflecting and rotating the face. // For this element, we know that the base transformations will be // permutation matrices. auto [trans, tshape] = lagrange.base_transformations(); // The matrices returned by `base_transformations` are quite large, and // are equal to the identity matrix except for a small block of the // matrix. It is often easier and more efficient to use the matrices // returned by the method `entity_transformations` instead. // `entity_transformations` returns a dictionary that maps the type // of entity (`"interval"`, `"triangle"`, `"quadrilateral"`) to a // matrix describing the effect of permuting that entity on the DOFs // on that entity. auto entity_transformation = lagrange.entity_transformations(); // For this element, we see that this method returns one matrix for // an interval: this matrix reverses the order of the four DOFs // associated with that edge. // In orders to work out which DOFs are associated with each edge, // we use the attribute `entity_dofs`. For example, the following can // be used to see which DOF numbers are associated with edge (dim 1) // number 2: int dim = 1; int edge_num = 2; std::vector entity_dofs = lagrange.entity_dofs()[dim][edge_num]; std::cout << std::endl << "Entity dofs of Edge number 2: "; for (const auto dof : entity_dofs) std::cout << dof << " "; } // Degree 2 Lagrange element // ========================= { // For a degree 2 Lagrange element, no permutations or transformations // are needed. std::cout << std::endl << std::endl << "Degree 2 Lagrange element" << std::endl; auto family = basix::element::family::P; auto cell_type = basix::cell::type::triangle; int degree = 2; auto variant = basix::element::lagrange_variant::equispaced; // Create the lagrange element auto lagrange = basix::create_element(family, cell_type, degree, variant, basix::element::dpc_variant::unset, false); // We can verify this by checking that`dof_transformations_are_identity` is // `True`. To confirm that the transformations are identity matrices, we // also print the base transformations. std::cout << "Dof transformations are identity: " << lagrange.dof_transformations_are_identity() << std::endl; std::cout << "Dof transformations are permutations: " << lagrange.dof_transformations_are_permutations() << std::endl; } // Degree 2 Nédélec element // ======================== { std::cout << std::endl << std::endl << "Degree 2 Nedelec element" << std::endl; // For a degree 2 Nédélec (first kind) element on a tetrahedron, the // corrections are not all permutations, so both // `dof_transformations_are_identity` and // `dof_transformations_are_permutations` are `False`. auto family = basix::element::family::N1E; auto cell_type = basix::cell::type::tetrahedron; int degree = 2; auto nedelec = basix::create_element( family, cell_type, degree, basix::element::lagrange_variant::unset, basix::element::dpc_variant::unset, false); std::cout << "Dof transformations are identity: " << nedelec.dof_transformations_are_identity() << std::endl; std::cout << "Dof transformations are permutations: " << nedelec.dof_transformations_are_permutations() << std::endl; // For this element, `entity_transformations` returns a map // with two entries: a matrix for an interval that describes // the effect of reversing the edge; and an array of two matrices // for a triangle. The first matrix for the triangle describes // the effect of rotating the triangle. The second matrix describes // the effect of reflecting the triangle. // For this element, the matrix describing the effect of rotating // the triangle is // .. math:: // \left(\begin{array}{cc}-1&-1\\1&0\end{array}\right). // This is not a permutation, so this must be applied when // assembling a form and cannot be applied to the DOF numbering in // the DOF map. const auto entity__transformation = nedelec.entity_transformations(); // To demonstrate how these transformations can be used, we create a // lattice of points where we will tabulate the element. const auto [pdata, shape] = basix::lattice::create( cell_type, 5, basix::lattice::type::equispaced, true); MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const T, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents> points(pdata.data(), shape); int num_points = points.extent(0); // If (for example) the direction of edge 2 in the physical cell // does not match its direction on the reference, then we need to // adjust the tabulated data. const auto [original_data, orig_shape] = nedelec.tabulate(0, points); auto [mod_data, mod_shape] = nedelec.tabulate(0, points); std::span data(mod_data.data(), mod_data.size()); // If the direction of edge 2 in the physical cell is reflected, it // has cell permutation info `....000010` so (from right to left): // - edge 0 is not permuted (0) // - edge 1 is reflected (1) // - edge 2 is not permuted (0) // - edge 3 is not permuted (0) // - edge 4 is not permuted (0) // - edge 5 is not permuted (0) int cell_info = 0b000010; nedelec.T_apply(data, num_points, cell_info); } return 0; } fenics-basix-0.9.0/demo/cpp/test.py000066400000000000000000000037141470517546000171500ustar00rootroot00000000000000"""Script for testing.""" import os import sys from subprocess import run import pytest # Get all the demos in this folder path = os.path.dirname(os.path.realpath(__file__)) demos = [] for folder in os.listdir(path): if folder.startswith("demo_"): subpath = os.path.join(path, folder) if os.path.isdir(subpath) and os.path.isfile(os.path.join(subpath, "main.cpp")): demos.append(folder) @pytest.fixture def cmake_args(request): return request.config.getoption("--cmake-args") @pytest.mark.parametrize("demo", demos) def test_demo(demo, cmake_args): """Test demos.""" demo_source = os.path.join(path, demo) demo_build = os.path.join(path, demo, "_build") # There is no cross-platform way to get cmake to execute a target. # See e.g. https://discourse.cmake.org/t/feature-request-cmake-run-target/9170 # TODO: Check existence of cross-platform target executer in cmake. if sys.platform.startswith("win32"): # Assume default generator is MSVC generator with multiple build targets run(f"cmake {cmake_args} -B {demo_build} -S {demo_source}", check=True, shell=True) # MSVC produces really slow binaries with --config Debug. run(f"cmake --build {demo_build} --config Release", check=True, shell=True) # MSVC generator supports multiple build targets per cmake configuration, each gets # its own subdirectory in {demo_build} e.g. Debug/ Release/ etc. demo_executable = demo + ".exe" run(os.path.join(demo_build, "Release", demo_executable), check=True, shell=True) else: # Uses default generator (usually make) run( f"cmake -DCMAKE_BUILD_TYPE=Debug {cmake_args} -B {demo_build} -S {demo_source}", check=True, shell=True, ) run(f"cmake --build {demo_build}", check=True, shell=True) demo_executable = demo run(os.path.join(demo_build, demo_executable), check=True, shell=True) fenics-basix-0.9.0/demo/python/000077500000000000000000000000001470517546000163515ustar00rootroot00000000000000fenics-basix-0.9.0/demo/python/convert_to_rst.py000066400000000000000000000003511470517546000217740ustar00rootroot00000000000000import os import pylit pylit.defaults.text_extensions = [".rst"] path = os.path.dirname(os.path.realpath(__file__)) for file in os.listdir(path): if file.endswith(".py") and file.startswith("demo"): pylit.main([file]) fenics-basix-0.9.0/demo/python/demo_create_and_tabulate.py000066400000000000000000000043771470517546000237100ustar00rootroot00000000000000# ================================== # Creating and tabulating an element # ================================== # # This demo shows how Basix can be used to create an element # and tabulate the values of its basis functions at a set of # points # # First, we import Basix and Numpy. import numpy as np import basix from basix import CellType, ElementFamily, LagrangeVariant # Next, we create a degree 4 Lagrange element on a quadrilateral using the function # `create_element`. The first input is the element family: for Lagrange elements, # we use `ElementFamily.P`. The second input is the cell type. The third # input is the degree of the element. For Lagrange elements, we must provide a # fourth input: the Lagrange variant. In this example, we use the equispaced # variant: this will place the degrees of freedom (DOFs) of the element in an # equally spaced lattice. lagrange = basix.create_element( ElementFamily.P, CellType.quadrilateral, 4, LagrangeVariant.equispaced ) # We now print the number of DOFs that this element has. print(lagrange.dim) # We see that the element has 25 DOFs: for this element, there will be 1 DOFs at # each vertex, 3 DOFs on each edge, and 9 DOFs on the interior of the quadrilateral. # # Next, we create a set of points as a numpy array, and tabulate the basis functions # of the Lagrange space at these points. The first input of `tabulate` is the number # of derivative to tabulate: we set this to 0 so will to compute the values of the # functions (and no derivatives). We pass in the points as the second input. points = np.array([[0.0, 0.0], [0.1, 0.1], [0.2, 0.3], [0.3, 0.6], [0.4, 1.0]]) tab = lagrange.tabulate(0, points) print(tab) print(tab.shape) # The result of tabulating is a 1 by 5 by 25 by 1 Numpy array. The first dimension # is 1 as we are only tabulating the function values; it would be higher if we # had asked for derivatives too. The second dimension (5) is the number of points. # The third dimension (25) is the number of DOFs. The fourth dimension (1) is the # value size of the element: this will be greater than 1 for vector-values elements. # # C++ demo # ======== # The following C++ code runs the same demo using Basix's C++ interface: # # .. literalinclude:: ../cpp/demo_create_and_tabulate/main.cpp # :language: c++ fenics-basix-0.9.0/demo/python/demo_custom_element.py000066400000000000000000000234501470517546000227560ustar00rootroot00000000000000# ========================= # Creating a custom element # ========================= # # In Basix, it is possible to create custom finite elements. This demo describes # how to do this and what data you need to provide when creating a custom element. # # First, we import Basix and Numpy. import numpy as np import basix from basix import CellType, LatticeType, MapType, PolynomialType, PolysetType, SobolevSpace # Lagrange element with bubble # ============================ # # As a first example, we create a degree 1 Lagrange element with a # quadratic bubble on a quadrilateral cell. This element will span the # following set of polynomials: # # .. math:: # \left\{1,\; y,\; x,\; xy,\; x(1-x)y(1-y)\right\}. # # We will define the degrees of freedom (DOFs) of this element by # placing a point evaluation at each vertex, plus one at the midpoint of # the cell. # # Polynomial coefficients # ----------------------- # # When creating a custom element, we must input the coefficients that # define a basis of the set of polynomials that our element spans. In # this example, we will represent the 5 functions above in terms of the # 9 orthogonal polynomials of degree :math:`\leqslant2` on a # quadrilateral, so we create a 5 by 9 matrix. wcoeffs = np.zeros((5, 9)) # The degree 2 orthonormal polynomials for a quadrilateral will have # their highest degree terms in the following order: # # .. math:: # 1,\; y,\; y^2,\; x,\; xy,\; xy^2,\; x^2,\; x^2y,\; x^2y^2 # # The order in which the polynomials appear in the orthonormal # polynomial sets for each cell are documented at # https://docs.fenicsproject.org/basix/main/polyset-order.html. # # As our polynomial space contains 1, :math:`y`, :math:`x` and # :math:`xy`. The first four rows of the matrix contain a single 1 for # the four orthogonal polynomials with these are their highest degree # terms. wcoeffs[0, 0] = 1 wcoeffs[1, 1] = 1 wcoeffs[2, 3] = 1 wcoeffs[3, 4] = 1 # The final row of the matrix defines the polynomials # :math:`x(1-x)y(1-y)`. As the polynomials are orthonormal, we can # represent this as # # .. math:: # x(1-x)y(1-y) = \sum_{i=0}^8\int_0^1\int_0^1p_i(x, y)x(1-x)y(1-y)\, # \mathrm{d}x\,\mathrm{d}y\; p_i(x, y), # # where :math:`p_0` to :math:`p_8` are the orthonormal polynomials. # Therefore the coefficients we want to put in the final row of our # matrix are: # # .. math:: # \int_0^1\int_0^1p_i(x, y)x(1-x)y(1-y)\,\mathrm{d}x\,\mathrm{d}y. # # We compute these integrals using a degree 4 quadrature rule (this is # the largest degree that the integrand will be, so these integrals will # be exact). pts, wts = basix.make_quadrature(CellType.quadrilateral, 4) poly = basix.tabulate_polynomials(PolynomialType.legendre, CellType.quadrilateral, 2, pts) x = pts[:, 0] y = pts[:, 1] f = x * (1 - x) * y * (1 - y) for i in range(9): wcoeffs[4, i] = sum(f * poly[i, :] * wts) # Interpolation # ------------- # # Next, we compute the points and matrices that define how functions can # be interpolated into this space. These are representations of the # functionals that are used in the Ciarlet definition of the finite # element -- in this example, these are evaluations at the points # described above. # # First, we define the points. We create an array of points for each # entity of each dimension. For each vertex of the cell, we include the # coordinates of that vertex. For the interior of the cell, we include a # point at :math:`(0.5,0.5)`. # # The shape of each of the point lists is (number of points, dimension). x = [[], [], [], []] x[0].append(np.array([[0.0, 0.0]])) x[0].append(np.array([[1.0, 0.0]])) x[0].append(np.array([[0.0, 1.0]])) x[0].append(np.array([[1.0, 1.0]])) x[2].append(np.array([[0.5, 0.5]])) # There are no DOFs associates with the edges for this element, so we # add an empty array of points for each edge. for _ in range(4): x[1].append(np.zeros((0, 2))) # We then define the interpolation matrices that define how the # evaluations at the points are combined to evaluate the functionals. As # all the DOFs are point evaluations in this example, the matrices are # all identity matrices for the entities that have a point. # # The shape of each matrix is (number of DOFs, value size, number of # points, number of derivatives). M = [[], [], [], []] for _ in range(4): M[0].append(np.array([[[[1.0]]]])) M[2].append(np.array([[[[1.0]]]])) # There are no DOFs associates with the edges for this element, so we add an empty # matrix for each edge. for _ in range(4): M[1].append(np.zeros((0, 1, 0, 1))) # Creating the element # -------------------- # # We now create the custom element. The inputs into `basix.create_custom_element` are: # # - The cell type. In this example, this is a quadrilateral. # - The value shape of the element. In this example, this is `[]` as the element is scalar. # - The coefficients that define the polynomial set. In this example, this is `wcoeffs`. # - The points used to define interpolation into the element. In this example, this is `x`. # - The matrix used to define interpolation into the element. In this example, this is `M`. # - The number of derivates used in the evaluation of the functionals. In this example, this # is 0. # - The map type. In this example, this is the identity map. # - The underlying Sobolev space. In this example, this is H1. # - A bool indicating whether the element is discontinuous. In this example, this is `False`. # - The highest degree :math:`n` such that all degree :math:`n` polynomials are contained in # this set. In this example, this is 1. # - The highest degree of a polynomial in the element. In this example, this is 2. It is # important that this value is correct, as it will be used to determine the number of # polynomials to use when creating and tabulating the element. # - The type of polynomial set to use. In this example, we use standard polynomials. element = basix.create_custom_element( CellType.quadrilateral, [], wcoeffs, x, M, 0, MapType.identity, SobolevSpace.H1, False, 1, 2, PolysetType.standard, ) # We can now use this element in the same way we can use a built-in element. For example, we # can tabulate the element at a set of points. If the points we use are the same as the points # we used to define the DOFs, we see that each basis function is equal to 1 at one of these points, # and equal to zero at all the other points. points = np.array([[0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [1.0, 1.0], [0.5, 0.5]]) print(element.tabulate(0, points)) # Degree 1 Ravairt--Thomas element # ================================ # # As a second example, we create a degree 1 Raviart--Thomas element on a triangle. Details of # the definition of this element can be found at # https://defelement.com/elements/raviart-thomas.html. This element # spans: # # .. math:: # \left\{(1, 0),\; (0, 1),\; (x, y)\right\}. # # The DOFs of this element are integrals along each edge of the cell of the dot product of the # function with the normal to the edge. # # In contrast to the scalar-valued element above, this element has a value size of 2. # # Polynomial coefficients # ----------------------- # # In this example, we will represent the 3 functions above in terms of the 3 orthogonal # polynomials of degree :math:`\leqslant1` on a triangle in each of the two coordinate directions. # We therefore create a 3 by 6 matrix. wcoeffs = np.zeros((3, 6)) # The highest degree terms in each polynomial will be: # # .. math:: # (1, 0),\; (y, 0),\; (x, 0),\; (0, 1),\; (0, y),\; (0, x) # # We include :math:`(1,0)` and :math:`(0,1)` as the first two rows of the matrix, and use # integrals to represent :math:`(x,y)` as in the previous example. wcoeffs[0, 0] = 1 wcoeffs[1, 3] = 1 pts, wts = basix.make_quadrature(CellType.triangle, 2) poly = basix.tabulate_polynomials(PolynomialType.legendre, CellType.triangle, 1, pts) x = pts[:, 0] y = pts[:, 1] for i in range(3): wcoeffs[2, i] = sum(x * poly[i, :] * wts) wcoeffs[2, 3 + i] = sum(y * poly[i, :] * wts) # Interpolation # ------------- # # For this element, there will be multiple points used per DOF, as the functionals that define # the element are integrals. We begin by defining a degree 1 quadrature rule on an interval. # This quadrature rule will be used to integrate on the edges of the triangle. pts, wts = basix.make_quadrature(CellType.interval, 1) # The points associated with each edge are calculated by mapping the quadrature points to each edge. x = [[], [], [], []] for _ in range(3): x[0].append(np.zeros((0, 2))) x[1].append(np.array([[1 - p[0], p[0]] for p in pts])) x[1].append(np.array([[0, p[0]] for p in pts])) x[1].append(np.array([[p[0], 0] for p in pts])) x[2].append(np.zeros((0, 2))) # The interpolation matrices for the edges in this example will be have shape (1, 2, len(pts), 1), # as there is one DOF per edge, the value size is 2, and we have len(pts) quadrature points on each # edge, and no extra derivatives are used. The entries of these matrices are the quadrature weights # multiplied by the normal directions. M = [[], [], [], []] for _ in range(3): M[0].append(np.zeros((0, 2, 0, 1))) for normal in [[-1, -1], [-1, 0], [0, 1]]: mat = np.empty((1, 2, len(wts), 1)) mat[0, 0, :, 0] = normal[0] * wts mat[0, 1, :, 0] = normal[1] * wts M[1].append(mat) M[2].append(np.zeros((0, 2, 0, 1))) # Creating the element # -------------------- element = basix.create_custom_element( CellType.triangle, [2], wcoeffs, x, M, 0, MapType.contravariantPiola, SobolevSpace.HDiv, False, 0, 1, PolysetType.standard, ) # To confirm that we have defined this element correctly, we compare it to the built-in # Raviart--Thomas element. rt = basix.create_element(basix.ElementFamily.RT, CellType.triangle, 1) points = basix.create_lattice(CellType.triangle, 1, LatticeType.equispaced, True) assert np.allclose(rt.tabulate(0, points), element.tabulate(0, points)) fenics-basix-0.9.0/demo/python/demo_custom_element_conforming_cr.py000066400000000000000000000116461470517546000256670ustar00rootroot00000000000000# ============================================== # Defining conforming Crouzeix--Raviart elements # ============================================== # # In this demo, we show how Basix's custom element functionality can be used to # create a conforming Crouzeix--Raviart element. # # First, we import Basix and Numpy. import matplotlib.pyplot as plt import numpy as np from mpl_toolkits import mplot3d # noqa: F401 import basix from basix import CellType, LatticeType, MapType, PolynomialType, PolysetType, SobolevSpace # Conforming CR element on a triangle # =================================== # # We begin by implementing this element on a triangle. The following function # implements this element for an arbitrary degree. Details of the definition of # this element can be found at # https://defelement.com/elements/conforming-crouzeix-raviart.html. # # As the input to this function, we use the degree of the element as shown on # DefElement. For most degrees, the highest degree polynomial in this elements # polynomial space is actually one degree higher, so we pass `degree + 1` into # `create_custom_element`. def create_ccr_triangle(degree): if degree == 1: wcoeffs = np.eye(3) x = [[], [], [], []] x[0].append(np.array([[0.0, 0.0]])) x[0].append(np.array([[1.0, 0.0]])) x[0].append(np.array([[0.0, 1.0]])) for _ in range(3): x[1].append(np.zeros((0, 2))) x[2].append(np.zeros((0, 2))) M = [[], [], [], []] for _ in range(3): M[0].append(np.array([[[1.0]]])) for _ in range(3): M[1].append(np.zeros((0, 1, 0))) M[2].append(np.zeros((0, 1, 0, 1))) return basix.create_custom_element( CellType.triangle, [], wcoeffs, x, M, 0, MapType.identity, SobolevSpace.L2, False, 1, 1, PolysetType.standard, ) npoly = (degree + 2) * (degree + 3) // 2 ndofs = degree * (degree + 5) // 2 wcoeffs = np.zeros((ndofs, npoly)) r = (degree + 1) * (degree + 2) // 2 wcoeffs[:r, :r] = np.eye(r) pts, wts = basix.make_quadrature(CellType.triangle, 2 * (degree + 1)) poly = basix.tabulate_polynomials(PolynomialType.legendre, CellType.triangle, degree + 1, pts) x = pts[:, 0] y = pts[:, 1] for i in range(1, degree): f = x**i * y ** (degree - i) * (x + y) for j in range(npoly): wcoeffs[r, j] = sum(f * poly[j, :] * wts) r += 1 geometry = basix.geometry(CellType.triangle) topology = basix.topology(CellType.triangle) x = [[], [], [], []] M = [[], [], [], []] for v in topology[0]: x[0].append(np.array(geometry[v])) M[0].append(np.array([[[[1.0]]]])) pts = basix.create_lattice(CellType.interval, degree, LatticeType.equispaced, False) mat = np.zeros((len(pts), 1, len(pts), 1)) mat[:, 0, :, 0] = np.eye(len(pts)) for e in topology[1]: v0 = geometry[e[0]] v1 = geometry[e[1]] edge_pts = [v0 + p * (v1 - v0) for p in pts] x[1].append(np.array(edge_pts)) M[1].append(mat) pts = basix.create_lattice(CellType.triangle, degree + 1, LatticeType.equispaced, False) x[2].append(pts) mat = np.zeros((len(pts), 1, len(pts), 1)) mat[:, 0, :, 0] = np.eye(len(pts)) M[2].append(mat) return basix.create_custom_element( CellType.triangle, [], wcoeffs, x, M, 0, MapType.identity, SobolevSpace.L2, False, degree, degree + 1, PolysetType.standard, ) # We can then create a degree 2 conforming CR element. e = create_ccr_triangle(2) # We now visualise the basis functions of the element we have created. pts = basix.create_lattice(CellType.triangle, 30, LatticeType.equispaced, True) x = pts[:, 0] y = pts[:, 1] z = e.tabulate(0, pts)[0] fig = plt.figure(figsize=(8, 8)) for n in range(7): if n == 6: ax = plt.subplot(3, 3, n + 2, projection="3d") else: ax = plt.subplot(3, 3, n + 1, projection="3d") ax.plot([0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0], "k-") ax.scatter(x, y, z[:, n, 0]) plt.savefig("ccr_triangle_2.png") # .. image:: ccr_triangle_2.png # :width: 100% # :alt: The basis functions of a degree 2 conforming CR element # We also visualise the basis functions of a degree 3 conforming CR element. e = create_ccr_triangle(3) pts = basix.create_lattice(CellType.triangle, 30, LatticeType.equispaced, True) x = pts[:, 0] y = pts[:, 1] z = e.tabulate(0, pts)[0] fig = plt.figure(figsize=(11, 8)) for n in range(12): ax = plt.subplot(3, 4, n + 1, projection="3d") ax.plot([0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0], "k-") ax.scatter(x, y, z[:, n, 0]) plt.savefig("ccr_triangle_3.png") # .. image:: ccr_triangle_3.png # :width: 100% # :alt: The basis functions of a degree 3 conforming CR element fenics-basix-0.9.0/demo/python/demo_dof_transformations.py000066400000000000000000000147731470517546000240240ustar00rootroot00000000000000# ==================================== # DOF permutations and transformations # ==================================== # # When using high degree finite elements on general meshes, adjustments # may need to be made to correct for differences in the orientation of # mesh entities on the mesh and on the reference cell. For example, in # a degree 4 Lagrange element on a triangle, there are 3 degrees of # freedom (DOFs) associated with each edge. If two neighbouring cells in # a mesh disagree on the direction of the edge, they could put an # incorrectly combine the local basis functions to give the wrong global # basis function. # # This issue and the use of permutations and transformations to correct # it is discussed in detail in `Construction of arbitrary order finite # element degree-of-freedom maps on polygonal and polyhedral cell # meshes (Scroggs, Dokken, Richardons, Wells, # 2021) `_. # # Functions to permute and transform high degree elements are # provided by Basix. In this demo, we show how these can be used. # # First, we import Basix and Numpy. import numpy as np import basix from basix import CellType, ElementFamily, LagrangeVariant, LatticeType # Degree 5 Lagrange element # ========================= # # We create a degree 5 Lagrange element on a triangle, then print the # values of the attributes `dof_transformations_are_identity` and # `dof_transformations_are_permutations`. # # The value of `dof_transformations_are_identity` is False: this tells # us that permutations or transformations are needed for this element. # # The value of `dof_transformations_are_permutations` is True: this # tells us that for this element, all the corrections we need to apply # permutations. This is the simpler case, and means we make the # orientation corrections by applying permutations when creating the # DOF map. lagrange = basix.create_element(ElementFamily.P, CellType.triangle, 5, LagrangeVariant.equispaced) print(lagrange.dof_transformations_are_identity) print(lagrange.dof_transformations_are_permutations) # We can apply permutations by using the matrices returned by the # method `base_transformations`. This method will return one matrix # for each edge of the cell (for 2D and 3D cells), and two matrices # for each face of the cell (for 3D cells). These describe the effect # of reversing the edge or reflecting and rotating the face. # # For this element, we know that the base transformations will be # permutation matrices. print(lagrange.base_transformations()) # The matrices returned by `base_transformations` are quite large, and # are equal to the identity matrix except for a small block of the # matrix. It is often easier and more efficient to use the matrices # returned by the method `entity_transformations` instead. # # `entity_transformations` returns a dictionary that maps the type # of entity (`"interval"`, `"triangle"`, `"quadrilateral"`) to a # matrix describing the effect of permuting that entity on the DOFs # on that entity. # # For this element, we see that this method returns one matrix for # an interval: this matrix reverses the order of the four DOFs # associated with that edge. print(lagrange.entity_transformations()) # In orders to work out which DOFs are associated with each edge, # we use the attribute `entity_dofs`. For example, the following can # be used to see which DOF numbers are associated with edge (dim 1) # number 2: print(lagrange.entity_dofs[1][2]) # Degree 2 Lagrange element # ========================= # # For a degree 2 Lagrange element, no permutations or transformations # are needed. We can verify this by checking that # `dof_transformations_are_identity` is `True`. To confirm that the # transformations are identity matrices, we also print the base # transformations. lagrange_degree_2 = basix.create_element( ElementFamily.P, CellType.triangle, 2, LagrangeVariant.equispaced ) print(lagrange_degree_2.dof_transformations_are_identity) print(lagrange_degree_2.base_transformations()) # Degree 2 Nédélec element # ======================== # # For a degree 2 Nédélec (first kind) element on a tetrahedron, the # corrections are not all permutations, so both # `dof_transformations_are_identity` and # `dof_transformations_are_permutations` are `False`. nedelec = basix.create_element(ElementFamily.N1E, CellType.tetrahedron, 2) print(nedelec.dof_transformations_are_identity) print(nedelec.dof_transformations_are_permutations) # For this element, `entity_transformations` returns a dictionary # with two entries: a matrix for an interval that describes # the effect of reversing the edge; and an array of two matrices # for a triangle. The first matrix for the triangle describes # the effect of rotating the triangle. The second matrix describes # the effect of reflecting the triangle. # # For this element, the matrix describing the effect of rotating # the triangle is # # .. math:: # \left(\begin{array}{cc}-1&-1\\1&0\end{array}\right). # # This is not a permutation, so this must be applied when assembling # a form and cannot be applied to the DOF numbering in the DOF map. print(nedelec.entity_transformations()) # To demonstrate how these transformations can be used, we create a # lattice of points where we will tabulate the element. points = basix.create_lattice(CellType.tetrahedron, 5, LatticeType.equispaced, True) # If (for example) the direction of edge 2 in the physical cell does # not match its direction on the reference, then we need to adjust the # tabulated data. # # As the cell sub-entity that we are correcting is an interval, we # get the `"interval"` item from the entity transformations dictionary. # We use `entity_dofs[1][2]` (1 is the dimension of an edge, 2 is the # index of the edge we are reversing) to find out which dofs are on # our edge. # # To adjust the tabulated data, we loop over each point in the lattice # and over the value size. For each of these values, we apply the # transformation matrix to the relevant DOFs. data = nedelec.tabulate(0, points) transformation = nedelec.entity_transformations()["interval"][0] dofs = nedelec.entity_dofs[1][2] for point in range(data.shape[1]): for dim in range(data.shape[3]): data[0, point, dofs, dim] = np.dot(transformation, data[0, point, dofs, dim]) print(data) # More efficient functions that apply the transformations and # permutations directly to data can be used via Basix's C++ # interface. # # C++ demo # ======== # The following C++ code runs the same demo using Basix's C++ interface: # # .. literalinclude:: ../cpp/demo_dof_transformations/main.cpp # :language: c++ fenics-basix-0.9.0/demo/python/demo_facet_integral.py000066400000000000000000000062761470517546000227110ustar00rootroot00000000000000# ========================== # Computing a facet integral # ========================== # # In this demo, we look at how Basix can be used to compute the integral # of the normal derivative of a basis function over a triangular facet # of a tetrahedral cell. # # As an example, we integrate the normal derivative of the fifth basis # function (note: counting starts at 0) of a degree 3 Lagrange space # over the zeroth facet of a tetrahedral cell. This facet will have # vertices at (1,0,0), (0,1,0) and (0,0,1). # # We start by importing Basis and Numpy. import numpy as np import basix from basix import CellType, ElementFamily, LagrangeVariant # We define a degree 3 Lagrange space on a tetrahedron. lagrange = basix.create_element( ElementFamily.P, CellType.tetrahedron, 3, LagrangeVariant.equispaced ) # The facets of a tetrahedron are triangular, so we create a quadrature # rule on a triangle. We use an order 3 rule so that we can integrate the # basis functions in our space exactly. points, weights = basix.make_quadrature(CellType.triangle, 3) # Next, we must map the quadrature points to our facet. We use the function # `geometry` to get the coordinates of the vertices of the tetrahedron, and # we use `sub_entity_connectivity` to see which vertices are adjacent to # our facet. We get the sub-entity connectivity using the indices 2 (facets # of 3D cells have dimension 2), 0 (vertices have dimension 0), and 0 (the # index of the facet we chose to use). # # Using this information, we can map the quadrature points to the facet. vertices = basix.geometry(CellType.tetrahedron) facet = basix.cell.sub_entity_connectivity(CellType.tetrahedron)[2][0][0] mapped_points = np.array( [ vertices[facet[0]] * (1 - x - y) + vertices[facet[1]] * x + vertices[facet[2]] * y for x, y in points ] ) # We now compute the normal derivative of the fifth basis function at # the quadrature points. First, we use `facet_outward_normals` to get # the normal vector to the facet. # # We then tabulate the basis functions of our space at the quadrature # points. We pass 1 in as the first argument, as we want the derivatives # of the basis functions. The result of tabulation will be an array of # size 4 by number of quadrature points by number of degrees of freedom. # To get the data that we want, we use the indices `1:` (to get the # derivatives and not also the function values), `:` (to include every # point), 5 (to get the fifth basis function), and 0 (to get the only # entry as the value size is 1). # # We then multiply the three derivatives of the basis function by # the three components of the normal. normal = basix.cell.facet_outward_normals(CellType.tetrahedron)[0] tab = lagrange.tabulate(1, mapped_points)[1:, :, 5, 0] normal_deriv = tab[0] * normal[0] + tab[1] * normal[1] + tab[2] * normal[2] # As our facet is not the reference triangle, we must multiply the # integrand by the norm of the Jacobian. We compute this by taking the # cross product of the two columns of the Jacobian, and then compute # the integral. jacobian = basix.cell.facet_jacobians(CellType.tetrahedron)[0] size_jacobian = np.linalg.norm(np.cross(jacobian[:, 0], jacobian[:, 1])) print(np.sum(normal_deriv * weights) * size_jacobian) fenics-basix-0.9.0/demo/python/demo_lagrange_variants.py000066400000000000000000000072051470517546000234220ustar00rootroot00000000000000# ============================= # Variants of Lagrange elements # ============================= # # When creating high degree function spaces, it is important to define the # degrees of freedom (DOFs) of a space in a way that leads to well behaved # basis functions. For example, if a Lagrange space is created using equally # spaced points, then the basis functions with exhibit Runge's phenomenon # and large peaks will be observed near the edges of the cell. # # When a finite element is defined using points evaluation DOFs, we can use # the Lebesgue constant to indicate how well behaved a set of basis functions # will be. The Lebesgue constant is defined by # # .. math:: # \Lambda = \max_{x\in R}\left(\sum_i\left|\phi_i(x)\right|\right), # # where :math:`R` is the reference cell and :math:`\phi_0` to :math:`\phi_{n-1}` # are the basis functions of the finite element space. A smaller value of # :math:`\Lambda` indicates a better set of basis functions. # # In this demo, we look at how the Lebesgue constant can be approximated using # Basix, and how variants of Lagrange elements that have lower Lebesgue constants # can be created. # # We begin by importing Basix and Numpy. import numpy as np import basix from basix import CellType, ElementFamily, LagrangeVariant, LatticeType # In this demo, we consider Lagrange elements defined on a triangle. We start # by creating a degree 15 Lagrange element that uses equally spaced points. # This element will exhibit Runge's phenomenon, so we expect a large Lebesgue # constant. lagrange = basix.create_element(ElementFamily.P, CellType.triangle, 15, LagrangeVariant.equispaced) # To estimate the Lebesgue constant, we create a lattice of points on the # triangle and compute # # .. math:: # \Lambda \approx \max_{x\in L}\left(\sum_i\left|\phi_i(x)\right|\right), # # where :math:`L` is the set of points in our lattice. As :math:`L` is a # subset of :math:`R`, the values we compute will be lower bounds of the true # Lebesgue constants. # # The function `create_lattice` takes four inputs: the cell type, the number # of points in each direction, the lattice type (in this example, we use an # equally spaced lattice), and a bool indicating whether or not points on the # boundary should be included. We tabulate our element at the points in the # lattice then use Numpy to compute the max of the sum. # # As expected, the value is large. points = basix.create_lattice(CellType.triangle, 50, LatticeType.equispaced, True) tab = lagrange.tabulate(0, points)[0] print(max(np.sum(np.abs(tab), axis=0))) # A Lagrange element with a lower Lebesgue constant can be created by placing # the DOFs at Gauss-Lobatto-Legendre (GLL) points. Passing # `LagrangeVariant.gll_warped` into `create_element` will make an element that # places its DOF points at warped GLL points on the triangle, as described in # `Nodal Discontinuous Galerkin Methods (Hesthaven, Warburton, 2008, pp # 175-180) `_. # # The Lebesgue constant for this variant of the element is much smaller than # for the equally spaced element. gll = basix.create_element(ElementFamily.P, CellType.triangle, 15, LagrangeVariant.gll_warped) print(max(np.sum(np.abs(gll.tabulate(0, points)[0]), axis=0))) # An even lower Lebesgue constant can be obtained by placing the DOF points # at GLL points mapped onto a triangle following the method proposed in # `Recursive, Parameter-Free, Explicitly Defined Interpolation Nodes for # Simplices (Isaac, 2020) `_. gll2 = basix.create_element(ElementFamily.P, CellType.triangle, 15, LagrangeVariant.gll_isaac) print(max(np.sum(np.abs(gll2.tabulate(0, points)[0]), axis=0))) fenics-basix-0.9.0/demo/python/demo_quadrature.py000066400000000000000000000067331470517546000221150ustar00rootroot00000000000000# ==================================== # Creating and using a quadrature rule # ==================================== # # This demo shows how quadrature rules can be obtained from Basix and how these # can be used to compute the integrals of functions. A quadrature rule uses a # set of points (:math:`p_0` to :math:`p_{n-1}`) and weights (:math:`w_0` to # :math:`w_{n-1}`), and approximates an integral as the weighted sum of the # values of the function at these points, ie # # .. math:: # # \int f\,\mathrm{d}x \approx \sum_i w_if(p_i). # # First, we import Basix and Numpy. import numpy as np import basix from basix import CellType, ElementFamily, LagrangeVariant # To get a quadrature rule on a triangle, we use the function `make_quadrature`. # This function takes two or three three inputs. We want to use the default # quadrature, pass in two inputs: a cell type and an order. The order of the rule # is equal to the degree of the highest degree polynomial that will be exactly # integrated by this rule. In this example, we use an order 4 rule, so all quartic # polynomials will be integrated exactly. # # `make_quadrature` returns two values: the points and the weights of the # quadrature rule. points, weights = basix.make_quadrature(CellType.triangle, 4) # If we want to control the type of quadrature used, we can pass in three # inputs to `make_quadrautre`. For example, the following code would force basix # to use a Gauss-Jacobi quadrature rule: points, weights = basix.make_quadrature( CellType.triangle, 4, rule=basix.QuadratureType.gauss_jacobi ) # We now use this quadrature rule to integrate the functions :math:`f(x,y)=x^3y` # and :math:`g(x,y)=x^3y^2` over the triangle. The exact values of these integrals # are 1/120 (0.00833333333333...) and 1/420 (0.00238095238095...) respectively. # # As :math:`f` is a degree 4 polynomial, we expect our quadrature rule to be able # to compute its integral exactly (within machine precision). :math:`g` on the # other hand is a degree 5 polynomial, so its integral will not be computed exactly. # # We define Python functions that compute :math:`f` and :math:`g` for every point. # These functions use features of Numpy to compute all the values at once. def f(points): return points[:, 0] ** 3 * points[:, 1] def g(points): return points[:, 0] ** 3 * points[:, 1] ** 2 # We can now use Numpy features to compute the integrals. print(np.sum(weights * f(points))) print(np.sum(weights * g(points))) # We obtain the values 0.00833333333333334 and 0.002393509368731209. As expected, # the integral of :math:`f` has been computed to within machine precision, while # the integral of :math:`g` is correct to 4 decimal places, but is not exact. # # Integrating a basis function # ============================ # # We next use the quadrature rule to compute the integral of a basis function in # a degree 3 Lagrange space. We first create the space and tabulate its basis # functions at the quadrature points. lagrange = basix.create_element(ElementFamily.P, CellType.triangle, 3, LagrangeVariant.equispaced) values = lagrange.tabulate(0, points) # We compute the integral of the third (note that the counting starts at 0) basis # function in this space. We can obtain the values of this basis function from # `values` by using the indices `[0, :, 3, 0]`. The integral can therefore # computed as follows. As this basis function will be degree three, the result # will again be exact (within machine precision). print(np.sum(weights * values[0, :, 3, 0])) fenics-basix-0.9.0/demo/python/index.rst000066400000000000000000000003751470517546000202170ustar00rootroot00000000000000===== Demos ===== .. toctree:: :maxdepth: 1 demo_create_and_tabulate.py demo_quadrature.py demo_facet_integral.py demo_lagrange_variants.py demo_dof_transformations.py demo_custom_element.py demo_custom_element_conforming_cr.py fenics-basix-0.9.0/demo/python/test.py000066400000000000000000000006641470517546000177100ustar00rootroot00000000000000import os import subprocess import sys import pytest # Get all the demos in this folder path = os.path.dirname(os.path.realpath(__file__)) demos = [] for file in os.listdir(path): if file.endswith(".py") and file.startswith("demo"): demos.append(file) @pytest.mark.parametrize("demo", demos) def test_demo(demo): ret = subprocess.run([sys.executable, demo], cwd=str(path), check=True) assert ret.returncode == 0 fenics-basix-0.9.0/doc/000077500000000000000000000000001470517546000146515ustar00rootroot00000000000000fenics-basix-0.9.0/doc/README.md000066400000000000000000000015141470517546000161310ustar00rootroot00000000000000Basix documentation =================== C++ documentation ----------------- To build the documentation of the Basix C++ library, run the following commands: ```bash cd cpp doxygen ``` The documentation will then be built in the folder `cpp/html`. Python documentation -------------------- To build the documentation of the Basix Python interface, run the following commands: ```bash cd python python -m sphinx -W -b html source/ build/html/ ``` The documentation will then be built in the folder `python/build/html`. Documentation for website ------------------------- To build the Basix documentation as it appears at [docs.fenicsproject.org/basix/main/](https://docs.fenicsproject.org/basix/main/), run the following commands: ```bash cd web python3 make_html.py ``` The documentation will then be built in the folder `web/html/`. fenics-basix-0.9.0/doc/cpp/000077500000000000000000000000001470517546000154335ustar00rootroot00000000000000fenics-basix-0.9.0/doc/cpp/Doxyfile000066400000000000000000003202001470517546000171360ustar00rootroot00000000000000# Doxyfile 1.8.13 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all text # before the first occurrence of this tag. Doxygen uses libiconv (or the iconv # built into libc) for the transcoding. See http://www.gnu.org/software/libiconv # for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = Basix # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = $(BASIX_VERSION) # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (ie a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, # C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: # FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: # Fortran. In the later case the parser tries to guess whether the code is fixed # or free formatted code, this is the default for Fortran type files), VHDL. For # instance to make doxygen treat .inc files as Fortran files (default is PHP), # and .f files as C (default is Fortran), use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. # Minimum value: 0, maximum value: 99, default value: 0. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 0 # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (ie std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # If one adds a struct or class to a group and this option is enabled, then also # any nested class or struct is added to the same group. By default this option # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (ie file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # (class|struct|union) declarations. If set to NO, these declarations will be # included in the documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file # names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = NO # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo # list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test # list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong or incomplete # parameter documentation, but not about the absence of documentation. # The default value is: NO. WARN_NO_PARAMDOC = NO # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. # The default value is: NO. WARN_AS_ERROR = YES # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = ../../cpp/basix index.md # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: http://www.gnu.org/software/libiconv) for the list of # possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, # *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, # *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. FILE_PATTERNS = *.cpp *.h *.md # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = ../../cpp/basix/loguru.hpp \ ../../cpp/basix/loguru.cpp \ ../../cpp/basix/mdspan # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (ie when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = index.md #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # function all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see http://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the config file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (ie in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES # If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the # clang parser (see: http://clang.llvm.org/) for more accurate parsing at the # cost of reduced performance. This can be particularly helpful with template # rich C++ code for which doxygen's built-in parser lacks the necessary type # information. # Note: The availability of this option depends on whether or not doxygen was # generated with the -Duse-libclang=ON option for CMake. # The default value is: NO. CLANG_ASSISTED_PARSING = NO # If clang assisted parsing is enabled you can provide the compiler with command # line options that you would normally use when invoking the compiler. Note that # the include paths will already be set by doxygen for the files and directories # specified with INPUT and INCLUDE_PATH. # This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. CLANG_OPTIONS = #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # http://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to YES can help to show when doxygen was last run and thus if the # documentation is up to date. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = NO # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: http://developer.apple.com/tools/xcode/), introduced with # OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the master .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (ie any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # http://www.mathjax.org) which uses client side Javascript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = YES # With MATHJAX_VERSION it is possible to specify the MathJax version to be used. # Note that the different versions of MathJax have different requirements with # regards to the different settings, so it is possible that also other MathJax # settings have to be changed when switching between the different MathJax # versions. # Possible values are: MathJax_2 and MathJax_3. # The default value is: MathJax_2. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_VERSION = MathJax_2 # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (ie MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from http://www.mathjax.org before deployment. # The default value is: http://cdn.mathjax.org/mathjax/latest. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = https://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /