diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3bdccd61..81ac2d39 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,6 +4,10 @@ updates: directory: "/" schedule: interval: "daily" + groups: + actions-deps: + patterns: + - "*" - package-ecosystem: "docker" directory: "/" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4527ad82..b8f19872 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -51,23 +51,23 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 + uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2 with: egress-policy: audit - name: Check out repo if: always() - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Pull ccache cache if: always() id: ccache-restore - uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 with: path: /home/runner/.cache/ccache key: ccache-${{ env.BUILD_IDENTIFIER }} - - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 if: always() with: python-version: ${{ matrix.py-version }} @@ -89,7 +89,7 @@ jobs: - name: Install if: always() - run: python -m pip list && make install PIP_INSTALL_FLAGS="--no-use-pep517 -vvv" + run: python -m pip list && make install PIP_INSTALL_FLAGS="--no-build-isolation -vvv" - name: Remove ccache cache if: ${{ steps.ccache-restore.outputs.cache-hit }} @@ -103,7 +103,7 @@ jobs: - name: Push ccache cache if: always() - uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 with: path: /home/runner/.cache/ccache key: ccache-${{ env.BUILD_IDENTIFIER }} @@ -129,7 +129,7 @@ jobs: mv cython-lint.xml cython-lint-${{ env.BUILD_IDENTIFIER }}.xml - name: Archive results - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 if: always() with: name: Results (${{ env.BUILD_PRETTY_IDENTIFIER }}) @@ -139,7 +139,7 @@ jobs: cython-lint-${{ env.BUILD_IDENTIFIER }}.xml - name: Report results - uses: dorny/test-reporter@dc3a92680fcc15842eef52e8c4606ea7ce6bd3f3 # v1 + uses: dorny/test-reporter@b082adf0eced0765477756c2a610396589b8c637 # v1 if: always() with: name: Report (${{ env.BUILD_PRETTY_IDENTIFIER }}) @@ -156,7 +156,7 @@ jobs: fail-fast: false name: Mac clang Python ${{ matrix.py-version }} - runs-on: macos-13 + runs-on: macos-15 timeout-minutes: 180 env: MPIEXEC_FLAGS: "--allow-run-as-root --oversubscribe" @@ -169,25 +169,25 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 + uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2 with: egress-policy: audit - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Pull ccache cache if: always() id: ccache-restore - uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 with: path: /Users/runner/Library/Caches/ccache key: ccache-${{ runner.os }}-${{ matrix.py-version }} - # - name: Setup GNU Fortran - # uses: fortran-lang/setup-fortran@2bbdcb313d2b7e72d1d07caf87f3ef9f376d9224 # v1.6.3 + - name: Setup GNU Fortran + uses: fortran-lang/setup-fortran@47809fdb6e637da656ce9ada436527b240c1287f # v1.8.1 - - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ matrix.py-version }} cache: 'pip' @@ -195,8 +195,9 @@ jobs: - name: Install packages run: | brew install open-mpi ccache - brew tap brewsci/num - brew install brewsci-metis brewsci-parmetis brewsci-suite-sparse + # brew tap brewsci/num + # brew install brewsci-metis brewsci-parmetis brewsci-suite-sparse + brew install scotch suite-sparse - name: Get ccache config dir if: always() @@ -211,7 +212,7 @@ jobs: run: make prereq && make prereq-extra - name: Install - run: PIP_INSTALL_FLAGS=-vvv make install + run: PIP_INSTALL_FLAGS="-vvv --no-build-isolation" make install - name: Remove ccache cache if: ${{ steps.ccache-restore.outputs.cache-hit }} @@ -225,7 +226,7 @@ jobs: - name: Push ccache cache if: always() - uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 with: path: /Users/runner/Library/Caches/ccache key: ccache-${{ runner.os }}-${{ matrix.py-version }} @@ -251,7 +252,7 @@ jobs: mv cython-lint.xml cython-lint-${{ runner.os }}-${{ matrix.py-version }}.xml - name: Archive results - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 if: always() with: name: Results ${{ github.job }} @@ -261,7 +262,7 @@ jobs: cython-lint-${{ runner.os }}-${{ matrix.py-version }}.xml - name: Report results - uses: dorny/test-reporter@dc3a92680fcc15842eef52e8c4606ea7ce6bd3f3 # v1 + uses: dorny/test-reporter@b082adf0eced0765477756c2a610396589b8c637 # v1 if: always() with: name: Report (${{ github.job }}) diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index 6916edd1..2363f2d4 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -31,7 +31,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 + uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2 with: egress-policy: audit @@ -43,7 +43,7 @@ jobs: - name: Check out if: always() - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 @@ -117,12 +117,12 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 + uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2 with: egress-policy: audit - name: Check out - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install podman-compose run: pip install podman-compose @@ -141,12 +141,12 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 + uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2 with: egress-policy: audit - name: Checkout binder branch - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: binder @@ -155,7 +155,7 @@ jobs: python generateDockerfile.py ${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.TEST_IMAGE_TAG }} - name: Create PR - uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 + uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 with: token: ${{ secrets.GITHUB_TOKEN }} commit-message: "Update Dockerfile" diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 03d08b62..def9e325 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -17,11 +17,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 + uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2 with: egress-policy: audit - name: 'Checkout Repository' - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: 'Dependency Review' - uses: actions/dependency-review-action@40c09b7dc99638e5ddb0bfd91c1673effc064d8a # v4.8.1 + uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2 diff --git a/.github/workflows/documentation-ci.yml b/.github/workflows/documentation-ci.yml index ab7f4490..6983b38c 100644 --- a/.github/workflows/documentation-ci.yml +++ b/.github/workflows/documentation-ci.yml @@ -24,21 +24,21 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 + uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2 with: egress-policy: audit - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Pull ccache cache id: ccache-restore - uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 with: path: /home/runner/.cache/ccache key: ccache-documentation-ci - - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3.13' cache: 'pip' @@ -52,7 +52,7 @@ jobs: run: make prereq && make prereq-extra - name: Install - run: make install PIP_INSTALL_FLAGS="--no-use-pep517 -vvv" + run: make dev - name: Remove ccache cache if: ${{ steps.ccache-restore.outputs.cache-hit }} @@ -65,7 +65,7 @@ jobs: continue-on-error: true - name: Push ccache cache - uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 with: path: /home/runner/.cache/ccache key: ccache-documentation-ci @@ -75,6 +75,6 @@ jobs: make docs - name: Upload artifact - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: path: 'docs/build' diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 182d9194..4c22ee95 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -25,21 +25,21 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 + uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2 with: egress-policy: audit - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Pull ccache cache id: ccache-restore - uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 with: path: /home/runner/.cache/ccache key: ccache - - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3.13' cache: 'pip' @@ -53,7 +53,7 @@ jobs: run: make prereq && make prereq-extra - name: Install - run: make install PIP_INSTALL_FLAGS="--no-use-pep517 -vvv" + run: make dev - name: Remove ccache cache if: ${{ steps.ccache-restore.outputs.cache-hit }} @@ -66,7 +66,7 @@ jobs: continue-on-error: true - name: Push ccache cache - uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 with: path: /home/runner/.cache/ccache key: ccache diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index ddd57a33..bb48ee09 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -29,12 +29,12 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 + uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2 with: egress-policy: audit - name: "Checkout code" - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -46,13 +46,13 @@ jobs: publish_results: true - name: "Upload artifact" - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: SARIF file path: results.sarif retention-days: 5 - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@16140ae1a102900babc80a33c44059580f687047 # v3.29.5 + uses: github/codeql-action/upload-sarif@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v3.29.5 with: sarif_file: results.sarif diff --git a/Dockerfile b/Dockerfile index b1a2ab92..cc62262a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,7 +30,6 @@ RUN sed -i 's/Components: main/Components: main contrib non-free/' /etc/apt/sour # allow running MPI as root in the container # bind MPI ranks to hwthreads ENV OMPI_MCA_hwloc_base_binding_policy=hwthread \ - PRTE_MCA_rmaps_default_mapping_policy=:oversubscribe \ MPIEXEC_FLAGS=--allow-run-as-root \ OMPI_ALLOW_RUN_AS_ROOT=1 \ OMPI_ALLOW_RUN_AS_ROOT_CONFIRM=1 \ @@ -52,7 +51,7 @@ RUN \ # Build PyNucleus # RUN --mount=type=cache,target=/root/.ccache --mount=type=cache,target=/root/.cache/pip \ RUN \ - make install PIP_INSTALL_FLAGS=" --break-system-packages" \ + make install PIP_INSTALL_FLAGS=" --break-system-packages --no-build-isolation" \ && find . -type f -name '*.c' -exec rm {} + \ && find . -type f -name '*.cpp' -exec rm {} + \ && rm -rf build packageTools/build base/build metisCy/build fem/build multilevelSolver/build nl/build \ diff --git a/Makefile b/Makefile index 82615d40..4fe175a1 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ PYTHON ?= python3 VIRTUAL_ENV ?= ifeq ($(VIRTUAL_ENV),) - FLAGS ?= --no-use-pep517 -e + FLAGS ?= --no-build-isolation PIP_FLAGS ?= --user else PYTHON ?= python diff --git a/base/PyNucleus_base/CSR_LinearOperator_{SCALAR}.pxi b/base/PyNucleus_base/CSR_LinearOperator_{SCALAR}.pxi index 7794b34a..909d5e09 100644 --- a/base/PyNucleus_base/CSR_LinearOperator_{SCALAR}.pxi +++ b/base/PyNucleus_base/CSR_LinearOperator_{SCALAR}.pxi @@ -306,6 +306,7 @@ cdef class {SCALAR_label}CSR_LinearOperator({SCALAR_label}LinearOperator): def copy(self): data = np.array(self.data, copy=True) other = {SCALAR_label}CSR_LinearOperator(self.indices, self.indptr, data) + other.num_columns = self.num_columns return other def sort_indices(self): diff --git a/base/PyNucleus_base/LinearOperator_{SCALAR}.pxi b/base/PyNucleus_base/LinearOperator_{SCALAR}.pxi index 0ee67b8d..d6be0abc 100644 --- a/base/PyNucleus_base/LinearOperator_{SCALAR}.pxi +++ b/base/PyNucleus_base/LinearOperator_{SCALAR}.pxi @@ -271,7 +271,7 @@ cdef class {SCALAR_label}LinearOperator: elif node.attrs['type'] == 'multiIntervalInterpolationOperator': return multiIntervalInterpolationOperator.HDF5read(node) elif node.attrs['type'] == 'h2': - from PyNucleus_nl.clusterMethodCy import H2Matrix + from PyNucleus_nl.clusterMethod import H2Matrix return H2Matrix.HDF5read(node) else: raise NotImplementedError(node.attrs['type']) diff --git a/base/PyNucleus_base/utilsFem.py b/base/PyNucleus_base/utilsFem.py index 5698e552..b321de96 100644 --- a/base/PyNucleus_base/utilsFem.py +++ b/base/PyNucleus_base/utilsFem.py @@ -468,11 +468,11 @@ def getSystemInfo(grp, argv=None, envVars=[('OMP_NUM_THREADS', True)]): if MPI.Is_initialized(): getMPIinfo(grp) getEnvVariables(grp, envVars) - import pkg_resources + import importlib from PyNucleus import subpackages versions = {} for pkg in ['numpy', 'scipy', 'mpi4py', 'cython']: - version = pkg_resources.get_distribution(pkg).version + version = importlib.metadata.version(pkg) try: versions[version].append(pkg) except KeyError: @@ -480,11 +480,9 @@ def getSystemInfo(grp, argv=None, envVars=[('OMP_NUM_THREADS', True)]): for version in versions: grp.add(','.join(versions[version]), version) - import importlib - versions = {} for pkg in sorted(subpackages.keys()): - version = pkg_resources.get_distribution('PyNucleus_'+pkg).version + version = importlib.metadata.version('PyNucleus_'+pkg) module = importlib.import_module('PyNucleus_'+pkg+'.config') sha = module.gitSHA try: @@ -1368,7 +1366,7 @@ def diffDict(d1, d2, aTol, relTol): return diff -def runDriver(path, py, python=None, timeout=900, ranks=None, cacheDir='', +def runDriver(path, py, python=None, timeout=600, ranks=None, cacheDir='', overwriteCache=False, aTol=1e-12, relTol=1e-2, extra=None): from subprocess import Popen, PIPE, TimeoutExpired diff --git a/docs/PyNucleus_nl.rst b/docs/PyNucleus_nl.rst index 13504b89..871f8263 100644 --- a/docs/PyNucleus_nl.rst +++ b/docs/PyNucleus_nl.rst @@ -6,10 +6,10 @@ Submodules ---------- -PyNucleus_nl.clusterMethodCy module +PyNucleus_nl.clusterMethod module ------------------------------------ -.. automodule:: PyNucleus_nl.clusterMethodCy +.. automodule:: PyNucleus_nl.clusterMethod PyNucleus_nl.config module @@ -59,12 +59,6 @@ PyNucleus_nl.kernels module .. automodule:: PyNucleus_nl.kernels -PyNucleus_nl.kernelsCy module ------------------------------ - -.. automodule:: PyNucleus_nl.kernelsCy - - PyNucleus_nl.nonlocalOperator module ------------------------------------------ diff --git a/drivers/testDistOp.py b/drivers/testDistOp.py index 2c049480..2c7a2a70 100755 --- a/drivers/testDistOp.py +++ b/drivers/testDistOp.py @@ -18,8 +18,8 @@ from PyNucleus_fem.mesh import plotManager from PyNucleus_base.utilsFem import TimerManager, timerOutputGroup from PyNucleus_nl import nonlocalPoissonProblem, FRACTIONAL -from PyNucleus_nl.clusterMethodCy import (DistributedH2Matrix_globalData, - DistributedH2Matrix_localData) +from PyNucleus_nl.clusterMethod import (DistributedH2Matrix_globalData, + DistributedH2Matrix_localData) import numpy as np diff --git a/drivers/variableOrder.py b/drivers/variableOrder.py index 5c83a45a..0c04d497 100755 --- a/drivers/variableOrder.py +++ b/drivers/variableOrder.py @@ -20,7 +20,7 @@ leftRightFractionalOrder, smoothedLeftRightFractionalOrder, innerOuterFractionalOrder) -from PyNucleus_nl.kernels import getFractionalKernel +from PyNucleus_nl.kernels import FractionalKernel d = driver() @@ -117,7 +117,7 @@ for s in sVals: b = dm.assembleRHS(rhs) err = None - kernel = getFractionalKernel(mesh.dim, s, horizon) + kernel = FractionalKernel.build(dim=mesh.dim, s=s, horizon=horizon) for label, do in [('dense', d.do_dense), ('dense_general', d.do_dense), @@ -134,7 +134,7 @@ elif label == 'H2': A = dm.assembleNonlocal(kernel, matrixFormat='H2') import matplotlib.pyplot as plt - # from fractionalLaplacian.clusterMethodCy import getFractionalOrders + # from fractionalLaplacian.clusterMethod import getFractionalOrders # if label == 'H2': # plt.figure() # A2 = builder.getDense() diff --git a/fem/PyNucleus_fem/DoFMaps.pyx b/fem/PyNucleus_fem/DoFMaps.pyx index 2279b2dd..da3137b5 100644 --- a/fem/PyNucleus_fem/DoFMaps.pyx +++ b/fem/PyNucleus_fem/DoFMaps.pyx @@ -833,7 +833,7 @@ cdef class DoFMap: """ try: - from PyNucleus_nl.kernelsCy import RangedFractionalKernel, ComplexKernel + from PyNucleus_nl.kernels import RangedFractionalKernel, ComplexKernel, MultiSingularityFractionalKernel if isinstance(kernel, RangedFractionalKernel): from PyNucleus_base.linear_operators import multiIntervalInterpolationOperator @@ -861,6 +861,11 @@ cdef class DoFMap: intervalOps.append(delayedNonlocalOp(self, gamma, matrixFormat=matrixFormat, dm2=dm2, **kwargs)) ops.append(intervalOps) return multiIntervalInterpolationOperator(intervals, nodes, ops) + elif isinstance(kernel, MultiSingularityFractionalKernel): + op = self.assembleNonlocal(kernel.kernels[0], matrixFormat=matrixFormat, dm2=dm2, **kwargs) + for i in range(1, len(kernel.kernels)): + op = op+self.assembleNonlocal(kernel.kernels[i], matrixFormat=matrixFormat, dm2=dm2, **kwargs) + return op elif isinstance(self, Product_DoFMap) and self.numComponents == 1: if dm2 is not None: return self.scalarDM.assembleNonlocal(kernel, matrixFormat, dm2.scalarDM, returnNearField, **kwargs) diff --git a/fem/PyNucleus_fem/factories.py b/fem/PyNucleus_fem/factories.py index 67259494..60180204 100644 --- a/fem/PyNucleus_fem/factories.py +++ b/fem/PyNucleus_fem/factories.py @@ -20,11 +20,6 @@ solCos1DHeat, rhsFunCos1DHeat, rhsFunSource1D, rhsFunSource2D, solCos2DHeat, rhsFunCos2DHeat, - solFractional, solFractionalDerivative, - rhsFractional1D, solFractional1D, - rhsFractional2D, solFractional2D, - solFractional2Dcombination, - rhsFractional2Dcombination, rhsHr, rhsHr2Ddisk, indicatorFunctor, @@ -49,18 +44,6 @@ solFichera = _solFichera() -def solFractional2D_nonPeriodic(s): - import numpy as np - return solFractional2Dcombination(s, [{'n': 2, 'l': 2, 'angular_shift': 0.}, - {'n': 1, 'l': 5, 'angular_shift': np.pi/3.}]) - - -def rhsFractional2D_nonPeriodic(s): - import numpy as np - return rhsFractional2Dcombination(s, [{'n': 2, 'l': 2, 'angular_shift': 0.}, - {'n': 1, 'l': 5, 'angular_shift': np.pi/3.}]) - - from . functions import (_rhsBoundaryLayer2D, _solBoundaryLayer2D, _solCornerSingularity2D, rhsMotor, rhsBoundarySingularity2D, solBoundarySingularity2D) @@ -75,12 +58,6 @@ def rhsFractional2D_nonPeriodic(s): functionFactory.register('solSin2D', _solSin2D, aliases=['sin2d']) functionFactory.register('solCos2D', _cos2D, aliases=['cos2d']) functionFactory.register('solSin3D', _solSin3D, aliases=['sin3d']) -functionFactory.register('solFractional', solFractional) -functionFactory.register('solFractionalDerivative', solFractionalDerivative) -functionFactory.register('solFractional1D', solFractional1D) -functionFactory.register('solFractional2D', solFractional2D) -functionFactory.register('rhsFractional1D', rhsFractional1D) -functionFactory.register('rhsFractional2D', rhsFractional2D) functionFactory.register('constant', constant) functionFactory.register('monomial', monomial) functionFactory.register('affine', affineFunction) diff --git a/fem/PyNucleus_fem/femCy.pyx b/fem/PyNucleus_fem/femCy.pyx index 84ae1d34..2328644e 100644 --- a/fem/PyNucleus_fem/femCy.pyx +++ b/fem/PyNucleus_fem/femCy.pyx @@ -16,11 +16,14 @@ from PyNucleus_base.ip_norm cimport mydot, vector_t, complex_vector_t from . meshCy cimport (meshBase, cells_t, vectorProduct, + volume0D, volume1Dnew, volume1D_in_2D, volume2Dnew, volume3D, volume3Dnew, volume2D_in_3D, + volume2D_in_3Dnew, + volume1D_in_3D, volume1Din2Dsimplex, volume1Din3Dsimplex) from . mesh import NO_BOUNDARY, meshNd @@ -37,7 +40,9 @@ from . DoFMaps cimport (P0_DoFMap, P1_DoFMap, P2_DoFMap, P3_DoFMap, from . quadrature cimport simplexQuadratureRule, Gauss1D, Gauss2D, Gauss3D, simplexXiaoGimbutas from . functions cimport function, complexFunction, vectorFunction, matrixFunction from . simplexMapper cimport simplexMapper +from . lookupFunction cimport lookupFunction from scipy.spatial import cKDTree +from scipy.integrate import nquad cdef class local_matrix_t: @@ -2958,6 +2963,126 @@ def assembleRHSgrad(FUNCTION_t fun, DoFMap dm, return dataVec +cdef class Integrand: + cdef: + function fun + DoFMap dm + INDEX_t dim + INDEX_t manifold_dim + REAL_t[:, ::1] simplex + REAL_t[:, ::1] span + REAL_t[::1] x + REAL_t[::1] bary + volume_t volume + REAL_t vol + shapeFunction phi + + def __init__(self, function fun, DoFMap dm): + self.fun = fun + self.dm = dm + + self.dim = dm.mesh.dim + self.manifold_dim = dm.mesh.manifold_dim + + self.simplex = uninitialized((self.manifold_dim+1, self.dim), + dtype=REAL) + self.span = uninitialized((self.manifold_dim, self.dim), dtype=REAL) + self.x = uninitialized((self.dim), dtype=REAL) + self.bary = uninitialized((self.manifold_dim+1), dtype=REAL) + + if self.manifold_dim == 0: + self.volume = volume0D + elif self.dim == 1 and self.manifold_dim == 1: + self.volume = volume1Dnew + elif self.dim == 2 and self.manifold_dim == 1: + self.volume = volume1D_in_2D + elif self.dim == 2 and self.manifold_dim == 2: + self.volume = volume2Dnew + elif self.dim == 3 and self.manifold_dim == 3: + self.volume = volume3D + elif self.dim == 3 and self.manifold_dim == 2: + self.volume = volume2D_in_3Dnew + elif self.dim == 3 and self.manifold_dim == 1: + self.volume = volume1D_in_3D + else: + raise NotImplementedError('dim={}, manifold_dim={}'.format(self.dim, self.manifold_dim)) + + cdef void setCell(self, cellNo): + cdef: + INDEX_t k, j + self.dm.mesh.getSimplex(cellNo, self.simplex) + # Calculate volume + for k in range(self.manifold_dim): + for j in range(self.dim): + self.span[k, j] = self.simplex[k+1, j]-self.simplex[0, j] + self.vol = self.volume(self.span) + + cdef void setDoF(self, dofNo): + self.phi = self.dm.getLocalShapeFunction(dofNo) + + def __call__(self, lam): + cdef: + INDEX_t i, j + REAL_t phiVal + if isinstance(lam, float): + lam = [lam] + self.x[:] = 0. + self.bary[self.manifold_dim] = 1. + for i in range(self.manifold_dim): + self.bary[i] = lam[i] + self.bary[self.manifold_dim] -= lam[i] + for j in range(self.dim): + self.x[j] += lam[i]*self.simplex[i, j] + for j in range(self.dim): + self.x[j] += self.bary[self.manifold_dim]*self.simplex[self.manifold_dim, j] + self.phi.evalPtr(&self.bary[0], NULL, &phiVal) + return self.vol*self.fun.eval(self.x)*phiVal + + +def assembleRHSWithSingularity(function fun, DoFMap dm, simplexQuadratureRule qr=None, fe_vector indicator=None, REAL_t desired_order=2., **kwargs): + cdef: + DoFMap dmP0 + INDEX_t cellNo, dofP0, k, dof + fe_vector data + + if indicator is not None: + dmP0 = indicator.dm + assert isinstance(dmP0, P0_DoFMap), dmP0 + assert dm.mesh == dmP0.mesh + else: + dmP0 = P0_DoFMap(dm.mesh, -1) + errorEstimate = (dmP0.assembleRHS(fun, qr=simplexXiaoGimbutas(dim=1, order=2)) + - dmP0.assembleRHS(fun, qr=simplexXiaoGimbutas(dim=1, order=4))) + errorEstimate.assign(np.absolute(errorEstimate.toarray())) + + indicator = dmP0.ones() + indicator.assign((errorEstimate.toarray() < dm.mesh.h**desired_order).astype(REAL)) + + ind = lookupFunction(dmP0.mesh, dmP0, indicator) + data = dm.assembleRHS(fun*ind, qr=qr) + + integrand = Integrand(fun, dm) + for cellNo in range(dm.mesh.num_cells): + dofP0 = dmP0.cell2dof(cellNo, 0) + assert indicator[dofP0] == 0. or indicator[dofP0] == 1. + if indicator[dofP0] == 0.: + integrand.setCell(cellNo) + for k in range(dm.dofs_per_element): + dof = dm.cell2dof(cellNo, k) + if dof < 0: + continue + + integrand.setDoF(k) + + # integrate in barycentric coords + if dm.mesh.manifold_dim == 1: + val, _ = nquad(integrand, ((0., 1.), ), **kwargs) + else: + raise NotImplementedError() + data[dof] += val + return data + + cdef class multi_function: def __init__(self, numInputs, numOutputs): self.numInputs = numInputs diff --git a/fem/PyNucleus_fem/functions.pyx b/fem/PyNucleus_fem/functions.pyx index 9aa0d9af..a3eb7889 100644 --- a/fem/PyNucleus_fem/functions.pyx +++ b/fem/PyNucleus_fem/functions.pyx @@ -7,7 +7,6 @@ from libc.math cimport (sin, cos, sinh, cosh, tanh, sqrt, atan2, M_PI as pi, pow, exp, floor, log2, log) -from scipy.special.cython_special cimport psi as digamma import numpy as np cimport numpy as np @@ -620,266 +619,6 @@ cdef class rhsTestGrayScott2D_V(function): return -u+2*pi**2*self.Dv*v-u*v**2+(self.k+self.F)*v -cdef class solFractional(function): - cdef public REAL_t s - cdef REAL_t fac, radius2 - cdef INDEX_t dim - - def __init__(self, REAL_t s, INDEX_t dim, REAL_t radius=1.0): - function.__init__(self) - from scipy.special import gamma - self.s = s - self.dim = dim - self.radius2 = radius**2 - self.fac = self.radius2**s * 2.**(-2.*s)*gamma(dim/2.)/gamma((dim+2.*s)/2.)/gamma(1.+s) - - cdef inline REAL_t eval(self, REAL_t[::1] x): - cdef REAL_t r2 = 0. - cdef INDEX_t i - for i in range(self.dim): - r2 += x[i]**2 - if r2 <= self.radius2: - return self.fac*pow(1.-r2/self.radius2, self.s) - else: - return 0. - - -cdef class solFractionalDerivative(function): - cdef public REAL_t s - cdef REAL_t fac, fac2, radius2 - cdef INDEX_t dim - - def __init__(self, REAL_t s, INDEX_t dim, REAL_t radius=1.0): - function.__init__(self) - from scipy.special import gamma - self.s = s - self.dim = dim - self.radius2 = radius**2 - self.fac = self.radius2**s * 2.**(-2.*s)*gamma(dim/2.)/gamma((dim+2.*s)/2.)/gamma(1.+s) - self.fac2 = log(0.25*self.radius2) - digamma(0.5*dim+s) - digamma(1+s) - - cdef inline REAL_t eval(self, REAL_t[::1] x): - cdef REAL_t r2 = 0. - cdef INDEX_t i - for i in range(self.dim): - r2 += x[i]**2 - if r2 <= self.radius2: - return (self.fac2+log(1.-r2/self.radius2))*self.fac*pow(1.-r2/self.radius2, self.s) - else: - return 0. - - -from scipy.special import eval_jacobi as jacobi - - -cdef class rhsFractional1D(function): - cdef: - public REAL_t s - REAL_t fac - public INDEX_t n - - def __init__(self, REAL_t s, INDEX_t n): - from scipy.special import gamma - function.__init__(self) - self.s = s - self.n = n - self.fac = 2.**(2.*s)*gamma(0.5+s+n)*gamma(1.+s+n)/gamma(1.+n)/gamma(0.5+n) - - cdef inline REAL_t eval(self, REAL_t[::1] x): - cdef REAL_t r2 = 0. - r2 = x[0]**2 - if r2 <= 1.: - return self.fac * jacobi(self.n, self.s, -0.5, 2.*r2-1.) - else: - return 0. - - -cdef class solFractional1D(function): - cdef: - public REAL_t s - public INDEX_t n - - def __init__(self, REAL_t s, INDEX_t n): - function.__init__(self) - self.s = s - self.n = n - - cdef inline REAL_t eval(self, REAL_t[::1] x): - cdef REAL_t r2 = 0. - r2 = x[0]**2 - if r2 <= 1.: - return (1.-r2)**self.s * jacobi(self.n, self.s, -0.5, 2.*r2-1.) - else: - return 0. - - -cdef class rhsFractional2D(function): - cdef: - public REAL_t s - public REAL_t angular_shift - public INDEX_t l - public INDEX_t n - REAL_t fac - - def __init__(self, REAL_t s, INDEX_t l, INDEX_t n, REAL_t angular_shift=0.): - function.__init__(self) - from scipy.special import gamma - self.s = s - self.l = l - self.n = n - self.angular_shift = angular_shift - self.fac = 2.**(2.*s)*gamma(1.+s+n)*gamma(1.+l+s+n)/gamma(1+n)/gamma(1.+l+n) - - cdef inline REAL_t eval(self, REAL_t[::1] x): - cdef REAL_t r2 = 0., theta = atan2(x[1], x[0]) - r2 = x[0]**2+x[1]**2 - if r2 <= 1.: - return self.fac*r2**(0.5*self.l)*cos(self.l*(theta+self.angular_shift))*jacobi(self.n, self.s, self.l, 2.*r2-1.) - else: - return 0. - - -cdef class solFractional2D(function): - cdef: - public REAL_t s - public REAL_t angular_shift - public INDEX_t l - public INDEX_t n - - def __init__(self, REAL_t s, INDEX_t l, INDEX_t n, REAL_t angular_shift=0.): - function.__init__(self) - self.s = s - self.l = l - self.n = n - self.angular_shift = angular_shift - - cdef inline REAL_t eval(self, REAL_t[::1] x): - cdef REAL_t r2 = 0., theta = atan2(x[1], x[0]) - r2 = x[0]**2+x[1]**2 - if r2 <= 1.: - return (1.-r2)**self.s*r2**(0.5*self.l)*cos(self.l*(theta+self.angular_shift))*jacobi(self.n, self.s, self.l, 2.*r2-1.) - else: - return 0. - - -cdef class rhsFractional2Dcombination(function): - cdef list functions - - def __init__(self, REAL_t s, params): - function.__init__(self) - self.functions = [rhsFractional2D(s, **p) for p in params] - - cdef inline REAL_t eval(self, REAL_t[::1] x): - cdef: - REAL_t val = 0. - INDEX_t i - function f - for i in range(len(self.functions)): - f = self.functions[i] - val += f.eval(x) - return val - - -cdef class solFractional2Dcombination(function): - cdef list functions - - def __init__(self, REAL_t s, params): - function.__init__(self) - self.functions = [solFractional2D(s, **p) for p in params] - - cdef inline REAL_t eval(self, REAL_t[::1] x): - cdef: - REAL_t val = 0. - INDEX_t i - function f - for i in range(len(self.functions)): - f = self.functions[i] - val += f.eval(x) - return val - - -cdef class rhsTestFractional_U(function): - cdef REAL_t t - cdef function sol - - def __init__(self, REAL_t s, INDEX_t dim, REAL_t t, REAL_t radius=1.0): - function.__init__(self) - self.sol = solFractional(s, dim, radius) - self.t = t - - cdef inline REAL_t eval(self, REAL_t[::1] x): - cdef REAL_t u = self.sol.eval(x) - return cos(self.t)*u + (cos(self.t)**2-sin(self.t)**2)*u**2 + sin(self.t) - - -cdef class rhsTestFractional_V(function): - cdef REAL_t t - cdef function sol - - def __init__(self, REAL_t s, INDEX_t dim, REAL_t t, REAL_t radius=1.0): - function.__init__(self) - self.sol = solFractional(s, dim, radius) - self.t = t - - cdef inline REAL_t eval(self, REAL_t[::1] x): - cdef REAL_t u = self.sol.eval(x) - return -sin(self.t)*u + (-cos(self.t)**2+sin(self.t)**2)*u**2 + cos(self.t) - - -cdef class rhsFractionalBrusselator_U(function): - cdef REAL_t t, B, Q, eta, radius2s - cdef function solU, solV - - def __init__(self, REAL_t s1, REAL_t s2, - REAL_t B, REAL_t Q, REAL_t eta, - INDEX_t dim, REAL_t t, REAL_t radius=1.0): - function.__init__(self) - self.solU = solFractional(s1, dim, radius) - self.solV = solFractional(s2, dim, radius) - self.B = B - self.Q = Q - self.eta = eta - self.t = t - self.radius2s = radius**(2.*s1) - - cdef inline REAL_t eval(self, REAL_t[::1] x): - cdef: - REAL_t u0 = self.solU.eval(x)*self.eta - REAL_t v0 = self.solV.eval(x)/self.eta - REAL_t s = sin(self.t) - REAL_t c = cos(2.*self.t) - REAL_t u = u0*s - REAL_t v = v0*c - return (cos(self.t)*u0) + s*self.eta/self.radius2s - ((self.B-1.)*u + self.Q**2*v + self.B/self.Q*u**2 + 2.*self.Q*u*v + u**2*v) - - -cdef class rhsFractionalBrusselator_V(function): - cdef REAL_t t, B, Q, eta, radius2s - cdef function solU, solV - - def __init__(self, REAL_t s1, REAL_t s2, - REAL_t B, REAL_t Q, REAL_t eta, - INDEX_t dim, REAL_t t, REAL_t radius=1.0): - function.__init__(self) - self.solU = solFractional(s1, dim, radius) - self.solV = solFractional(s2, dim, radius) - self.B = B - self.Q = Q - self.eta = eta - self.t = t - self.radius2s = radius**(2.*s2) - - cdef inline REAL_t eval(self, REAL_t[::1] x): - cdef: - REAL_t u0 = self.solU.eval(x)*self.eta - REAL_t v0 = self.solV.eval(x)/self.eta - REAL_t s = sin(self.t) - REAL_t c = cos(2.*self.t) - REAL_t u = u0*s - REAL_t v = v0*c - return self.eta**2*(-2.*sin(2.*self.t)*v0) + c/self.eta/self.radius2s + (self.B*u + self.Q**2*v + self.B/self.Q*u**2 + 2.*self.Q*u*v + u**2*v) - - cdef class simpleAnisotropy(function): cdef REAL_t epsilon diff --git a/fem/PyNucleus_fem/lookupFunction.pxd b/fem/PyNucleus_fem/lookupFunction.pxd index e56d8a23..fc1d4cc7 100644 --- a/fem/PyNucleus_fem/lookupFunction.pxd +++ b/fem/PyNucleus_fem/lookupFunction.pxd @@ -34,8 +34,18 @@ cdef class vectorLookupFunction(vectorFunction): cdef class UniformLookup1D(function): cdef: - REAL_t a - REAL_t b - REAL_t[::1] vals + public REAL_t a + public REAL_t b + public REAL_t[::1] vals REAL_t dx REAL_t invDx + REAL_t outOfBoundsValue + + +cdef class Lookup1D(function): + cdef: + public REAL_t[::1] x + public REAL_t[::1] vals + public str kind + REAL_t outOfBoundsValue + object interp diff --git a/fem/PyNucleus_fem/lookupFunction.pyx b/fem/PyNucleus_fem/lookupFunction.pyx index 875fa499..73c6b625 100644 --- a/fem/PyNucleus_fem/lookupFunction.pyx +++ b/fem/PyNucleus_fem/lookupFunction.pyx @@ -6,11 +6,10 @@ ################################################################################### import numpy as np -from PyNucleus_base.myTypes import REAL, INDEX +from PyNucleus_base.myTypes import REAL from PyNucleus_base.blas import uninitialized from . DoFMaps cimport shapeFunction from . femCy cimport simplexComputations1D, simplexComputations2D, simplexComputations3D -from libc.math cimport floor cdef class lookupFunction(function): @@ -86,20 +85,51 @@ cdef class vectorLookupFunction(vectorFunction): cdef class UniformLookup1D(function): - def __init__(self, REAL_t a, REAL_t b, REAL_t[::1] vals): + def __init__(self, REAL_t a, REAL_t b, REAL_t[::1] vals, REAL_t outOfBoundsValue=np.nan): self.a = a self.b = b self.vals = vals self.dx = (b-a)/(self.vals.shape[0]-1) self.invDx = 1./self.dx + self.outOfBoundsValue = outOfBoundsValue cdef REAL_t eval(self, REAL_t[::1] x): cdef: INDEX_t k REAL_t theta - k = max(min(INDEX((x[0]-self.a)*self.invDx), self.vals.shape[0]-2), 0) + k = ((x[0]-self.a)*self.invDx) theta = (x[0]-self.a-k*self.dx)*self.invDx - return (1-theta)*self.vals[k] + theta * self.vals[k+1] + if 0 <= k and k < self.vals.shape[0]: + return (1-theta)*self.vals[k] + theta * self.vals[k+1] + elif k == self.vals.shape[0]-1 and theta < 1e-12: + return self.vals[self.vals.shape[0]-1] + else: + return self.outOfBoundsValue def __reduce__(self): return UniformLookup1D, (self.a, self.b, np.array(self.vals)) + + def __repr__(self): + return '{}([{}, {}], {})'.format(self.__class__.__name__, self.a, self.b, self.dx) + + +from scipy.interpolate import interp1d + + +cdef class Lookup1D(function): + def __init__(self, REAL_t[::1] x, REAL_t[::1] vals, str kind, REAL_t outOfBoundsValue=np.nan): + assert x.shape[0] == vals.shape[0] + self.x = x + self.vals = vals + self.kind = kind + self.outOfBoundsValue = outOfBoundsValue + self.interp = interp1d(self.x, self.vals, kind=kind, fill_value=outOfBoundsValue) + + cdef REAL_t eval(self, REAL_t[::1] x): + return self.interp(x[0]) + + def __reduce__(self): + return Lookup1D, (np.array(self.x), np.array(self.vals), self.kind, self.outOfBoundsValue) + + def __repr__(self): + return '{}({})'.format(self.__class__.__name__, self.kind) diff --git a/fem/PyNucleus_fem/meshCy.pxd b/fem/PyNucleus_fem/meshCy.pxd index 510cf189..68412a1c 100644 --- a/fem/PyNucleus_fem/meshCy.pxd +++ b/fem/PyNucleus_fem/meshCy.pxd @@ -65,6 +65,7 @@ cdef REAL_t volume0D(REAL_t[:, ::1] span) cdef REAL_t volume1D(REAL_t[::1] v0) cdef REAL_t volume1Dnew(REAL_t[:, ::1] span) cdef REAL_t volume1D_in_2D(REAL_t[:, ::1] span) +cdef REAL_t volume1D_in_3D(REAL_t[:, ::1] span) cdef REAL_t volume2D(REAL_t[::1] v0, REAL_t[::1] v1) cdef REAL_t volume2Dnew(REAL_t[:, ::1] span) cdef REAL_t volume2D_in_3D(REAL_t[::1] v0, REAL_t[::1] v1) diff --git a/fem/PyNucleus_fem/meshCy.pyx b/fem/PyNucleus_fem/meshCy.pyx index 855140a8..e543da57 100644 --- a/fem/PyNucleus_fem/meshCy.pyx +++ b/fem/PyNucleus_fem/meshCy.pyx @@ -1557,6 +1557,10 @@ cdef REAL_t volume2D(REAL_t[::1] v0, REAL_t[::1] v1): return abs(v0[0]*v1[1]-v1[0]*v0[1])*0.5 +cdef REAL_t volume1D_in_3D(REAL_t[:, ::1] span): + return sqrt(span[0, 0]**2+span[0, 1]**2+span[0, 2]**2) + + cdef REAL_t volume2D_in_3D(REAL_t[::1] v0, REAL_t[::1] v1): cdef: REAL_t temp_mem[3] diff --git a/fem/PyNucleus_fem/orthogonalPolynomials.pxd b/fem/PyNucleus_fem/orthogonalPolynomials.pxd new file mode 100644 index 00000000..6c69c184 --- /dev/null +++ b/fem/PyNucleus_fem/orthogonalPolynomials.pxd @@ -0,0 +1,32 @@ +################################################################################### +# Copyright 2021 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. # +# If you want to use this code, please refer to the README.rst and LICENSE files. # +################################################################################### + +from . functions cimport function +from PyNucleus_base.myTypes cimport REAL_t + + +cdef class IP: + cdef: + function weight + REAL_t a + REAL_t b + + +cdef class LegendreShiftedIP(IP): + pass + + +cdef class JacobiShiftedIP(IP): + pass + + +cdef class LogJacobiShiftedIP(IP): + pass + + +cdef class LogAffineJacobiShiftedIP(IP): + pass diff --git a/fem/PyNucleus_fem/orthogonalPolynomials.pyx b/fem/PyNucleus_fem/orthogonalPolynomials.pyx new file mode 100644 index 00000000..3eb13434 --- /dev/null +++ b/fem/PyNucleus_fem/orthogonalPolynomials.pyx @@ -0,0 +1,268 @@ +################################################################################### +# Copyright 2021 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. # +# If you want to use this code, please refer to the README.rst and LICENSE files. # +################################################################################### + +import numpy as np +cimport numpy as np +from libc.math cimport sqrt, log +from PyNucleus_base.myTypes import REAL +from PyNucleus_base.myTypes cimport INDEX_t +from PyNucleus_base.blas cimport uninitializedREAL +from scipy.integrate import quad +from scipy.special._orthogonal import _gen_roots_and_weights +from . quadrature cimport quadQuadratureRule +from itertools import product + + +cdef class Polynomial(function): + cdef: + public REAL_t[::1] coeffcients + + def __init__(self, coeffcients): + super(Polynomial, self).__init__() + if not isinstance(coeffcients, np.ndarray): + coeffcients = np.array(coeffcients) + self.coeffcients = coeffcients + + @property + def degree(self): + return self.coeffcients.shape[0]-1 + + cdef REAL_t eval(self, REAL_t[::1] x): + cdef: + INDEX_t k + REAL_t val = 0. + for k in range(self.degree+1): + val += self.coeffcients[k]*pow(x[0], k) + return val + + def __repr__(self): + s = [] + c = self.coeffcients[0] + if abs(c) > 0: + s.append('{}'.format(c)) + for i in range(1, self.degree+1): + c = self.coeffcients[i] + if abs(c) > 0: + if c == 1.0: + s.append('x^{}'.format(i)) + else: + s.append('{}*x^{}'.format(c, i)) + return ' + '.join(s) + + def __mul__(self, other): + if isinstance(other, Polynomial) and isinstance(self, Polynomial): + newCoeffs = np.zeros((self.degree+other.degree+1)) + for i in range(self.degree+1): + for j in range(other.degree+1): + newCoeffs[i+j] += self.coeffcients[i]*other.coeffcients[j] + return Polynomial(newCoeffs) + elif isinstance(other, float) and isinstance(self, Polynomial): + return Polynomial(np.array(self.coeffcients)*other) + elif isinstance(other, Polynomial) and isinstance(self, float): + return Polynomial(np.array(other.coeffcients)*self) + else: + raise NotImplementedError() + + def __add__(self, other): + newCoefficients = np.zeros((max(self.degree, other.degree)+1)) + newCoefficients[:self.degree+1] += np.array(self.coeffcients) + newCoefficients[:other.degree+1] += np.array(other.coeffcients) + return Polynomial(newCoefficients) + + def __sub__(self, other): + newCoefficients = np.zeros((max(self.degree, other.degree)+1)) + newCoefficients[:self.degree+1] += np.array(self.coeffcients) + newCoefficients[:other.degree+1] -= np.array(other.coeffcients) + return Polynomial(newCoefficients) + + def __rmul__(self, other): + if isinstance(other, Polynomial) and isinstance(self, Polynomial): + newCoeffs = np.zeros((self.degree+other.degree+1)) + for i in range(self.degree+1): + for j in range(other.degree+1): + newCoeffs[i+j] += self.coeffcients[i]*other.coeffcients[j] + return Polynomial(newCoeffs) + else: + return Polynomial(np.array(self.coeffcients)*other) + + def __truediv__(self, other): + return Polynomial(np.array(self.coeffcients)/other) + + def diff(self): + return Polynomial(np.array(self.coeffcients)[1:]*np.arange(1, self.degree+1)) + + +cdef class Integrand: + cdef: + function f + function g + function weight + REAL_t[::1] xA + + def __init__(self, function f, function g, function weight): + self.f = f + self.g = g + self.weight = weight + self.xA = np.empty((1), dtype=REAL) + + cdef REAL_t eval(self, REAL_t x): + self.xA[0] = x + return self.f.eval(self.xA)*self.g.eval(self.xA)*self.weight.eval(self.xA) + + def __call__(self, REAL_t x): + return self.eval(x) + + +cdef class IP: + def __init__(self, function weight, REAL_t a, REAL_t b): + self.weight = weight + self.a = a + self.b = b + + def __call__(self, function f, function g): + cdef: + Integrand I + I = Integrand(f, g, self.weight) + return quad(I, self.a, self.b)[0] + + def __repr__(self): + return "{} weighted inner product on ({}, {})".format(self.weight, self.a, self.b) + + +cdef class LegendreWeightShifted(function): + cdef: + REAL_t alpha + REAL_t beta + + cdef REAL_t eval(self, REAL_t[::1] x): + return 1. + + def __repr__(self): + return '1' + + +cdef class LegendreShiftedIP(IP): + def __init__(self): + ip = LegendreWeightShifted() + super(LegendreShiftedIP, self).__init__(ip, 0., 1.) + + +cdef class JacobiWeightShifted(function): + cdef: + REAL_t alpha + REAL_t beta + + def __init__(self, REAL_t alpha, REAL_t beta): + assert alpha > -1., "alpha needs to be > -1." + assert beta > -1., "beta needs to be > -1." + self.alpha = alpha + self.beta = beta + + cdef REAL_t eval(self, REAL_t[::1] x): + return pow(x[0], self.alpha)*pow(1.-x[0], self.beta) + + def __repr__(self): + return "x**{} * (1-x)**{}".format(self.alpha, self.beta) + + +cdef class JacobiShiftedIP(IP): + def __init__(self, REAL_t alpha, REAL_t beta): + ip = JacobiWeightShifted(alpha, beta) + super(JacobiShiftedIP, self).__init__(ip, 0., 1.) + + +cdef class LogJacobiWeightShifted(function): + cdef: + REAL_t alpha + REAL_t beta + REAL_t gamma + + def __init__(self, REAL_t alpha, REAL_t beta, REAL_t gamma): + assert alpha > -1., "alpha needs to be > -1." + assert beta > -1., "beta needs to be > -1." + self.alpha = alpha + self.beta = beta + self.gamma = gamma + + cdef REAL_t eval(self, REAL_t[::1] x): + return pow(x[0], self.alpha)*pow(1.-x[0], self.beta)*pow(-log(x[0]), self.gamma) + + def __repr__(self): + return "x**{} * (1-x)**{} * (-log(x))**{}".format(self.alpha, self.beta, self.gamma) + + +cdef class LogJacobiShiftedIP(IP): + def __init__(self, REAL_t alpha, REAL_t beta, REAL_t gamma): + ip = LogJacobiWeightShifted(alpha, beta, gamma) + super(LogJacobiShiftedIP, self).__init__(ip, 0., 1.) + + +class QuadRuleBuilder: + def __init__(self, ip): + self.ip = ip + self.p = [] + self.normP = [] + self.a = [] + self.b = [] + + def computeBasis(self, maxDegree): + if len(self.p) == 0: + qNew = Polynomial([1.]) + normQ = sqrt(self.ip(qNew, qNew)) + self.normP.append(normQ) + self.p.append(qNew) + + x = Polynomial([0., 1.]) + while len(self.p) <= maxDegree: + qNew = x*self.p[len(self.p)-1] + self.a.append(self.ip(qNew, self.p[len(self.p)-1])/self.normP[len(self.normP)-1]**2) + if len(self.p) >= 2: + self.b.append(self.ip(qNew, self.p[len(self.p)-2])/self.normP[len(self.normP)-2]**2) + qNew = qNew - self.a[len(self.a)-1]*self.p[len(self.p)-1] - self.b[len(self.b)-1]*self.p[len(self.p)-2] + else: + qNew = qNew - self.a[len(self.a)-1]*self.p[len(self.p)-1] + normQ = sqrt(self.ip(qNew, qNew)) + self.normP.append(normQ) + self.p.append(qNew) + + def build(self, INDEX_t maxDegree): + self.computeBasis(maxDegree) + + a_s = np.array(self.a) + b_s = np.array(self.b) + + def an_func(n): + n = n.astype(int) + return a_s[n] + + def bn_func(n): + n = n.astype(int)-1 + return np.sqrt(b_s[n]) + + def eval(n, x): + poly = self.p[n] + xA = np.empty((1), dtype=REAL) + y = np.empty_like(x) + for i in range(x.shape[0]): + xA[0] = x[i] + y[i] = poly(xA) + return y + + def eval_diff(n, x): + poly = self.p[n].diff() + xA = np.empty((1), dtype=REAL) + y = np.empty_like(x) + for i in range(x.shape[0]): + xA[0] = x[i] + y[i] = poly(xA) + return y + + one = Polynomial([1.]) + mu0 = self.ip(one, one) + nodes, weights = _gen_roots_and_weights(maxDegree, mu0, an_func, bn_func, eval, eval_diff, False, False) + qr = nodes, weights + return qr diff --git a/fem/PyNucleus_fem/quadrature.pxd b/fem/PyNucleus_fem/quadrature.pxd index c0607a36..6b7b42ee 100644 --- a/fem/PyNucleus_fem/quadrature.pxd +++ b/fem/PyNucleus_fem/quadrature.pxd @@ -15,6 +15,7 @@ from . meshCy cimport (vectorProduct, volume2Dnew, volume3D, volume3Dnew, volume2D_in_3Dnew as volume2D_in_3D, + volume1D_in_3D, meshBase) cimport numpy as np from libc.math cimport sqrt @@ -164,3 +165,13 @@ cdef class sphericalQuadRule2D(sphericalQuadRule): cdef class simplexJaskowiecSukumar(simplexQuadratureRule): cdef public INDEX_t order + + +cdef class GaussJacobi_2(quadQuadratureRule): + cdef: + public INDEX_t order + + +cdef class LogGaussJacobi(quadQuadratureRule): + cdef: + public INDEX_t order diff --git a/fem/PyNucleus_fem/quadrature.pyx b/fem/PyNucleus_fem/quadrature.pyx index 08d8e451..9f2053e6 100644 --- a/fem/PyNucleus_fem/quadrature.pyx +++ b/fem/PyNucleus_fem/quadrature.pyx @@ -5,13 +5,16 @@ # If you want to use this code, please refer to the README.rst and LICENSE files. # ################################################################################### -from PyNucleus_base.myTypes import REAL +from PyNucleus_base.myTypes import REAL, BOOL from PyNucleus_base import uninitialized from PyNucleus_base.blas cimport uninitializedREAL from libc.math cimport sin, cos, M_PI as pi import numpy as np from modepy import XiaoGimbutasSimplexQuadrature from modepy.tools import unit_to_barycentric +from . orthogonalPolynomials cimport LegendreShiftedIP, JacobiShiftedIP, LogJacobiShiftedIP +from . orthogonalPolynomials import QuadRuleBuilder +from libc.math cimport log cdef class quadratureRule: @@ -61,8 +64,10 @@ cdef class simplexQuadratureRule(quadratureRule): self.volume = volume3D elif self.dim == 3 and self.manifold_dim == 2: self.volume = volume2D_in_3D + elif self.dim == 3 and self.manifold_dim == 1: + self.volume = volume1D_in_3D else: - raise NotImplementedError('dim={}'.format(self.dim)) + raise NotImplementedError('dim={}, manifold_dim={}'.format(self.dim, self.manifold_dim)) self.span = uninitialized((self.manifold_dim, self.dim), dtype=REAL) self.tempVec = uninitializedREAL((self.dim, )) @@ -598,3 +603,97 @@ cdef class simplexJaskowiecSukumar(simplexQuadratureRule): dim, manifold_dim) else: raise NotImplementedError() + + +def tensorProduct(REAL_t[:, ::1] nodes0, + REAL_t[::1] weights0, + REAL_t[:, ::1] nodes1, + REAL_t[::1] weights1): + cdef: + INDEX_t dim0 = nodes0.shape[0] + INDEX_t dim1 = nodes1.shape[0] + INDEX_t numNodes0 = nodes0.shape[1] + INDEX_t numNodes1 = nodes1.shape[1] + INDEX_t i, j, k, l + REAL_t[:, ::1] productNodes + REAL_t[::1] productWeights + + productNodes = uninitializedREAL((dim0+dim1, numNodes0*numNodes1)) + productWeights = uninitializedREAL((numNodes0*numNodes1, )) + + k = 0 + for i in range(numNodes0): + for j in range(numNodes1): + for l in range(dim0): + productNodes[l, k] = nodes0[l, i] + for l in range(dim1): + productNodes[dim0+l, k] = nodes1[l, j] + productWeights[k] = weights0[i]*weights1[j] + k += 1 + return productNodes, productWeights + + +cdef class GaussJacobi_2(quadQuadratureRule): + def __init__(self, order_weight_exponents): + nodes1D = [] + weights1D = [] + dim = len(order_weight_exponents) + self.orders = [] + for i, (order, alpha, beta) in enumerate(order_weight_exponents): + k = (order+1)//2 + if 2*k-1 != order: + # print('Incrementing order in Gauss-Jacobi quadrature rule, only odd orders are available.') + k += 1 + self.orders.append(2*k-1) + + if abs(alpha) == 0. and abs(beta) == 0.: + ip = LegendreShiftedIP() + else: + ip = JacobiShiftedIP(alpha, beta) + qrb = QuadRuleBuilder(ip) + n1D, w1D = qrb.build(k) + n1D = n1D[np.newaxis, :] + + if i == 0: + nodes = n1D + weights = w1D + else: + nodes, weights = tensorProduct(nodes, weights, n1D, w1D) + + super(GaussJacobi_2, self).__init__(nodes, weights) + self.order = order + + +cdef class LogGaussJacobi(quadQuadratureRule): + def __init__(self, order_weight_exponents): + nodes1D = [] + weights1D = [] + dim = len(order_weight_exponents) + self.orders = [] + + for i, (order, alpha, beta, gamma) in enumerate(order_weight_exponents): + k = (order+1)//2 + if 2*k-1 != order: + # print('Incrementing order in Gauss-Jacobi quadrature rule, only odd orders are available.') + k += 1 + self.orders.append(2*k-1) + + if abs(gamma) == 0: + if abs(alpha) == 0. and abs(beta) == 0.: + ip = LegendreShiftedIP() + else: + ip = JacobiShiftedIP(alpha, beta) + else: + ip = LogJacobiShiftedIP(alpha, beta, gamma) + qrb = QuadRuleBuilder(ip) + n1D, w1D = qrb.build(k) + n1D = n1D[np.newaxis, :] + + if i == 0: + nodes = n1D + weights = w1D + else: + nodes, weights = tensorProduct(nodes, weights, n1D, w1D) + + super(LogGaussJacobi, self).__init__(nodes, weights) + self.order = order diff --git a/fem/setup.py b/fem/setup.py index 0feb2a4b..a38b3bde 100644 --- a/fem/setup.py +++ b/fem/setup.py @@ -61,6 +61,8 @@ sources=[p.folder+"DoFMaps.pyx"]) p.addExtension("lookupFunction", sources=[p.folder+"lookupFunction.pyx"]) +p.addExtension("orthogonalPolynomials", + sources=[p.folder+"orthogonalPolynomials.pyx"]) p.addExtension("quadrature", sources=[p.folder+"quadrature.pyx"]) p.addExtension("meshOverlaps", @@ -78,6 +80,6 @@ p.setup(description="A finite element code.", install_requires=['Cython>=0.29.32', 'numpy', 'scipy>=1.13', 'matplotlib', 'meshpy', 'modepy', - 'gmsh_interop', + 'gmsh_interop', 'gmsh', 'mpi4py>=4.0.0', 'PyNucleus_base']) diff --git a/nl/PyNucleus_nl/__init__.py b/nl/PyNucleus_nl/__init__.py index f19f7495..10213831 100644 --- a/nl/PyNucleus_nl/__init__.py +++ b/nl/PyNucleus_nl/__init__.py @@ -14,21 +14,21 @@ It allows to assemble nonlocal operators as dense, sparse of H^2 matrices. """ -from . kernelsCy import (Kernel, - FractionalKernel, - RangedFractionalKernel, - getKernelEnum, - FRACTIONAL, INDICATOR, PERIDYNAMIC, GAUSSIAN) +from . kernels import (Kernel, + FractionalKernel, + RangedFractionalKernel, + getKernelEnum, + FRACTIONAL, INDICATOR, PERIDYNAMIC, GAUSSIAN) from . nonlocalAssembly import nonlocalBuilder -from . clusterMethodCy import H2Matrix +from . clusterMethod import H2Matrix +from . factories import (nonlocalMeshFactory, + interactionFactory, + fractionalOrderFactory, + kernelFactory, + twoPointFunctionFactory) from . nonlocalProblems import (fractionalLaplacianProblem, nonlocalPoissonProblem, - transientFractionalProblem, - twoPointFunctionFactory, - fractionalOrderFactory, - interactionFactory, - kernelFactory, - nonlocalMeshFactory) + transientFractionalProblem) from . discretizedProblems import (discretizedNonlocalProblem, discretizedTransientProblem) __all__ = ['twoPointFunctionFactory', 'fractionalOrderFactory', 'interactionFactory', 'kernelFactory', 'nonlocalMeshFactory', diff --git a/nl/PyNucleus_nl/clusterMethodCy.pxd b/nl/PyNucleus_nl/clusterMethod.pxd similarity index 99% rename from nl/PyNucleus_nl/clusterMethodCy.pxd rename to nl/PyNucleus_nl/clusterMethod.pxd index e905adaa..d170cfba 100644 --- a/nl/PyNucleus_nl/clusterMethodCy.pxd +++ b/nl/PyNucleus_nl/clusterMethod.pxd @@ -22,7 +22,7 @@ from PyNucleus_base.performanceLogger cimport PLogger, FakePLogger from PyNucleus_fem.DoFMaps cimport DoFMap from PyNucleus_fem.meshCy cimport meshBase from . fractionalOrders cimport fractionalOrderBase -from . kernelsCy cimport Kernel, FractionalKernel +from . kernels cimport Kernel, FractionalKernel cdef class transferMatrixBuilder: diff --git a/nl/PyNucleus_nl/clusterMethodCy.pyx b/nl/PyNucleus_nl/clusterMethod.pyx similarity index 98% rename from nl/PyNucleus_nl/clusterMethodCy.pyx rename to nl/PyNucleus_nl/clusterMethod.pyx index 96d8bead..8fb18bd3 100644 --- a/nl/PyNucleus_nl/clusterMethodCy.pyx +++ b/nl/PyNucleus_nl/clusterMethod.pyx @@ -23,6 +23,7 @@ from . nonlocalAssembly cimport nearFieldClusterPair from PyNucleus_fem.DoFMaps cimport DoFMap, P0_DoFMap, P1_DoFMap, P2_DoFMap, P3_DoFMap, shapeFunction from PyNucleus_fem.meshCy cimport meshBase from PyNucleus_fem.functions cimport constant +from . kernelNormalization cimport constantFractionalLaplacianScaling, variableFractionalLaplacianScaling import mpi4py.rc mpi4py.rc.initialize = False from mpi4py import MPI @@ -2166,6 +2167,23 @@ def assembleFarFieldInteractions(Kernel kernel, dict Pfar, BOOL_t bemMode=False) INDEX_t kiSize1, kiSize2 productIterator pit BOOL_t kernel_variable = kernel.variable + BOOL_t multiLogKernel = False + REAL_t fac + INDEX_t termNo + REAL_t[::1] scaling_values + + if isinstance(kernel.scalingPrePhi, constantFractionalLaplacianScaling): + scal = kernel.scalingPrePhi + if scal.values.shape[0] > 1: + multiLogKernel = True + termNo = kernel.termNo + scaling_values = scal.values + elif isinstance(kernel.scalingPrePhi, variableFractionalLaplacianScaling): + scalVar = kernel.scalingPrePhi + if scalVar.values.shape[0] > 1: + multiLogKernel = True + termNo = kernel.termNo + scaling_values = scalVar.values for lvl in Pfar: for cP in Pfar[lvl]: @@ -2213,13 +2231,17 @@ def assembleFarFieldInteractions(Kernel kernel, dict Pfar, BOOL_t bemMode=False) if kernel_variable: kernel.evalParamsPtr(dim, &x[i, 0], &y[j, 0]) kernel.evalPtr(dim, &x[i, 0], &y[j, 0], &cP.kernelInterpolant[i, j]) - cP.kernelInterpolant[i, j] *= -2.0 + cP.kernelInterpolant[i, j] *= -2.0*0.5 + if multiLogKernel: + cP.kernelInterpolant[i, j] *= scaling_values[termNo] else: for i in range(kiSize1): for j in range(kiSize2): if kernel_variable: kernel.evalParamsPtr(dim, &x[i, 0], &y[j, 0]) kernel.evalPtr(dim, &x[i, 0], &y[j, 0], &cP.kernelInterpolant[i, j]) + if multiLogKernel: + cP.kernelInterpolant[i, j] *= scaling_values[termNo] else: cP.kernelInterpolantVec = uninitialized((kiSize1, kiSize2, kernel.valueSize), dtype=REAL) if not bemMode: @@ -2229,13 +2251,21 @@ def assembleFarFieldInteractions(Kernel kernel, dict Pfar, BOOL_t bemMode=False) kernel.evalParamsPtr(dim, &x[i, 0], &y[j, 0]) kernel.evalPtr(dim, &x[i, 0], &y[j, 0], &cP.kernelInterpolantVec[i, j, 0]) for l in range(kernel.valueSize): - cP.kernelInterpolantVec[i, j, l] *= -2.0 + cP.kernelInterpolantVec[i, j, l] *= -2.0*0.5 + if multiLogKernel: + fac = scaling_values[termNo] + for l in range(kernel.valueSize): + cP.kernelInterpolantVec[i, j, l] *= fac else: for i in range(kiSize1): for j in range(kiSize2): if kernel_variable: kernel.evalParamsPtr(dim, &x[i, 0], &y[j, 0]) kernel.evalPtr(dim, &x[i, 0], &y[j, 0], &cP.kernelInterpolantVec[i, j, 0]) + if multiLogKernel: + fac = scaling_values[termNo] + for l in range(kernel.valueSize): + cP.kernelInterpolantVec[i, j, l] *= fac cdef class H2Matrix(LinearOperator): @@ -3123,6 +3153,21 @@ cdef class VectorH2Matrix(VectorLinearOperator): Z[dof1, dof2] = 1 plt.pcolormesh(Z) + def getOp(self, INDEX_t opNo): + cdef: + farFieldClusterPair cP + assert 0 <= opNo < self.vectorSize + + newPfar = {} + for lvl in self.Pfar: + newPfar[lvl] = [] + for cP in self.Pfar[lvl]: + newcP = farFieldClusterPair(cP.n1, cP.n2) + newcP.kernelInterpolant = np.ascontiguousarray(cP.kernelInterpolantVec[:, :, opNo]) + newPfar[lvl].append(newcP) + self.tree.prepareTransferOperators(1) + return H2Matrix(self.tree, newPfar, self.Anear.getOp(opNo), self.PLogger) + cdef class DistributedH2Matrix_globalData(LinearOperator): """ diff --git a/nl/PyNucleus_nl/discretizedProblems.py b/nl/PyNucleus_nl/discretizedProblems.py index 498a05c1..e663920c 100644 --- a/nl/PyNucleus_nl/discretizedProblems.py +++ b/nl/PyNucleus_nl/discretizedProblems.py @@ -25,7 +25,7 @@ from . nonlocalProblems import (DIRICHLET, NEUMANN, HOMOGENEOUS_NEUMANN, transientFractionalProblem) -from . clusterMethodCy import H2Matrix, DistributedH2Matrix_globalData, DistributedH2Matrix_localData +from . clusterMethod import H2Matrix, DistributedH2Matrix_globalData, DistributedH2Matrix_localData import logging @@ -552,7 +552,7 @@ def getOperators(self, hierarchy): @generates('A_derivative') def getDerivativeOperator(self, kernel, dmInterior, matrixFormat, eta, target_order): - self.A_derivative = dmInterior.assembleNonlocal(kernel.getDerivativeKernel(derivative=1), + self.A_derivative = dmInterior.assembleNonlocal(kernel.getGradientKernel(), matrixFormat=matrixFormat, params={'eta': eta, 'target_order': target_order}) @@ -684,14 +684,13 @@ def adjointSolve(self, b, dm, dmInterior, P_interior, adjointSolver, tol, maxite self.adjointModelSolution = stationaryModelSolution(self, u, **data) def report(self, group): - group.add('kernel', repr(self.continuumProblem.kernel)) - group.add('kernel expression', self.continuumProblem.kernel.getLongDescription()) - group.add('problem', self.continuumProblem.problemDescription) - group.add('has analytic solution', self.continuumProblem.analyticSolution is not None) + from . kernels import Kernel + self.continuumProblem.report(group) group.add('h', self.finalMesh.h) group.add('hmin', self.finalMesh.hmin) if self.continuumProblem.kernel is not None: - group.add('horizon', self.continuumProblem.kernel.horizonValue) + if isinstance(self.continuumProblem.kernel, Kernel): + group.add('horizon', self.continuumProblem.kernel.horizonValue) else: group.add('horizon', 0.0) group.add('mesh quality', self.finalMesh.delta) diff --git a/nl/PyNucleus_nl/factories.py b/nl/PyNucleus_nl/factories.py new file mode 100644 index 00000000..3a05afa8 --- /dev/null +++ b/nl/PyNucleus_nl/factories.py @@ -0,0 +1,360 @@ +################################################################################### +# Copyright 2021 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. # +# If you want to use this code, please refer to the README.rst and LICENSE files. # +################################################################################### + +from PyNucleus_base.factory import factory +import numpy as np +from PyNucleus_base import REAL + +from PyNucleus_fem.mesh import (simpleInterval, + intervalWithInteraction, + uniformSquare, squareWithInteractions, + discWithInteraction, + gradedDiscWithInteraction, + graded_interval, + # double_graded_interval, + double_graded_interval_with_interaction, + discWithIslands, + twinDisc, + # box, + # boxWithInteractions, + ball) +from PyNucleus_fem.functions import (Lambda, constant, + squareIndicator, radialIndicator) +from PyNucleus_fem.mesh import meshFactory as meshFactoryClass +from PyNucleus_fem.DoFMaps import P1_DoFMap +from PyNucleus_fem import (PHYSICAL, NO_BOUNDARY, + DIRICHLET, HOMOGENEOUS_DIRICHLET, + NEUMANN, HOMOGENEOUS_NEUMANN, + NORM) +from . twoPointFunctions import (constantTwoPoint, + temperedTwoPoint, + leftRightTwoPoint, + interfaceTwoPoint, + lambdaTwoPoint, + lookupTwoPoint) +from . interactionDomains import (fullSpace, + ball1_retriangulation, + ball1_barycenter, + ball2_retriangulation, + ball2_barycenter, + ballInf_retriangulation, + ballInf_barycenter, + ellipse_retriangulation, + ellipse_barycenter) +from . fractionalOrders import (constFractionalOrder, + variableConstFractionalOrder, + constantNonSymFractionalOrder, + leftRightFractionalOrder, + linearLeftRightFractionalOrder, + smoothedLeftRightFractionalOrder, + innerOuterFractionalOrder, + smoothedInnerOuterFractionalOrder, + islandsFractionalOrder, + layersFractionalOrder, + singleVariableUnsymmetricFractionalOrder, + feFractionalOrder) +from . kernels import (Kernel, + FractionalKernel, + FRACTIONAL, INDICATOR, PERIDYNAMIC, GAUSSIAN, EXPONENTIAL, POLYNOMIAL, + LOGINVERSEDISTANCE, MONOMIAL, + ) +from PyNucleus_fem import functionFactory +from . nonlocal_functions import (solFractional, + rhsFractional1D, solFractional1D, + rhsFractional2D, solFractional2D, + solFractional2Dcombination, + rhsFractional2Dcombination, + solutionArbitraryOrder, + rhsArbitraryOrder) + + +def solFractional2D_nonPeriodic(s): + import numpy as np + return solFractional2Dcombination(s, [{'n': 2, 'l': 2, 'angular_shift': 0.}, + {'n': 1, 'l': 5, 'angular_shift': np.pi/3.}]) + + +def rhsFractional2D_nonPeriodic(s): + import numpy as np + return rhsFractional2Dcombination(s, [{'n': 2, 'l': 2, 'angular_shift': 0.}, + {'n': 1, 'l': 5, 'angular_shift': np.pi/3.}]) + + + + +class fractionalOrderFactoryClass(factory): + def build(self, name, *args, **kwargs): + dm = None + if 'dm' in kwargs: + dm = kwargs.pop('dm') + if dm is not None: + s = self.build(name, *args, **kwargs) + assert isinstance(s, (constFractionalOrder, variableConstFractionalOrder, + constantNonSymFractionalOrder, singleVariableUnsymmetricFractionalOrder)) + sVec = dm.interpolate(s.fixedY(np.zeros((dm.mesh.dim), dtype=REAL))) + return super().build('fe', sVec, s.min, s.max) + else: + return super().build(name, *args, **kwargs) + + +fractionalOrderFactory = fractionalOrderFactoryClass() +fractionalOrderFactory.register('constant', constFractionalOrder, aliases=['const']) +fractionalOrderFactory.register('varConst', variableConstFractionalOrder, aliases=['constVar', 'constantSym']) +fractionalOrderFactory.register('leftRight', leftRightFractionalOrder, aliases=['twoDomain']) +fractionalOrderFactory.register('linearLeftRightNonSym', linearLeftRightFractionalOrder) +fractionalOrderFactory.register('smoothedLeftRight', smoothedLeftRightFractionalOrder, params={'r': 0.1, 'slope': 200.}, aliases=['twoDomainNonSym']) +fractionalOrderFactory.register('constantNonSym', constantNonSymFractionalOrder) +fractionalOrderFactory.register('innerOuter', innerOuterFractionalOrder) +fractionalOrderFactory.register('innerOuterNonSym', smoothedInnerOuterFractionalOrder) +fractionalOrderFactory.register('islands', islandsFractionalOrder, params={'r': 0.1, 'r2': 0.6}) +fractionalOrderFactory.register('layers', layersFractionalOrder) +fractionalOrderFactory.register('fe', feFractionalOrder) + +twoPointFunctionFactory = factory() +twoPointFunctionFactory.register('constant', constantTwoPoint, aliases=['const', 'constantTwoPoint']) +twoPointFunctionFactory.register('tempered', temperedTwoPoint, aliases=['temperedTwoPoint']) +twoPointFunctionFactory.register('leftRight', leftRightTwoPoint, aliases=['leftRightTwoPoint']) +twoPointFunctionFactory.register('interface', interfaceTwoPoint, aliases=['interfaceTwoPoint']) +twoPointFunctionFactory.register('lambda', lambdaTwoPoint) +twoPointFunctionFactory.register('lookup', lookupTwoPoint) + +interactionFactory = factory() +interactionFactory.register('fullSpace', fullSpace, aliases=['full']) +interactionFactory.register('ball2_retriangulation', ball2_retriangulation, aliases=['ball2', '2', 2]) +interactionFactory.register('ball2_barycenter', ball2_barycenter) +interactionFactory.register('ball1_retriangulation', ball1_retriangulation, aliases=['ball1', '1', 1]) +interactionFactory.register('ball1_barycenter', ball1_barycenter) +interactionFactory.register('ballInf_retriangulation', ballInf_retriangulation, aliases=['ballInf', 'inf', np.inf]) +interactionFactory.register('ballInf_barycenter', ballInf_barycenter) +interactionFactory.register('ellipse_retriangulation', ellipse_retriangulation, aliases=['ellipse']) +interactionFactory.register('ellipse_barycenter', ellipse_barycenter) + + +def getKernel(dim, + s=None, + horizon=None, + scaling=None, + interaction=None, + normalized=True, + piecewise=True, + phi=None, + kernel=FRACTIONAL, + monomialPower=np.nan, + exponentInverse=np.nan, + max_horizon=np.nan, + variance=1., + exponentialRate=1.0): + from . kernels import (_getKernelType, + MANIFOLD_FRACTIONAL) + kType = _getKernelType(kernel) + if kType == FRACTIONAL: + return FractionalKernel.build(dim=dim, s=s, horizon=horizon, interaction=interaction, scaling=scaling, normalized=normalized, piecewise=piecewise, phi=phi, max_horizon=max_horizon) + else: + return Kernel.build(dim=dim, + kernel=kType, + horizon=horizon, + scaling=scaling, + interaction=interaction, + normalized=normalized, + piecewise=piecewise, + phi=phi, + max_horizon=max_horizon, + monomialPower=monomialPower, + variance=variance, + exponentialRate=exponentialRate) +kernelFactory = factory() +kernelFactory.register('fractional', FractionalKernel.build) +kernelFactory.register('indicator', Kernel.build, params={'kernel': INDICATOR}, aliases=['constant']) +kernelFactory.register('inverseDistance', Kernel.build, params={'kernel': PERIDYNAMIC}, aliases=['peridynamic', 'inverseOfDistance']) +kernelFactory.register('gaussian', Kernel.build, params={'kernel': GAUSSIAN}) +kernelFactory.register('exponential', Kernel.build, params={'kernel': EXPONENTIAL}) +kernelFactory.register('polynomial', Kernel.build, params={'kernel': POLYNOMIAL}) +kernelFactory.register('logInverseDistance', Kernel.build, params={'kernel': LOGINVERSEDISTANCE}) +kernelFactory.register('monomial', Kernel.build, params={'kernel': MONOMIAL}) + + +class nonlocalMeshFactoryClass(factory): + def __init__(self): + super(nonlocalMeshFactoryClass, self).__init__() + self.nonOverlappingMeshFactory = meshFactoryClass() + self.overlappingMeshFactory = meshFactoryClass() + + def register(self, name, classTypeNoOverlap, classTypeOverlap, dim, indicators, paramsNoOverlap={}, paramsOverlap={}, aliases=[]): + if classTypeNoOverlap is not None: + self.nonOverlappingMeshFactory.register(name, classTypeNoOverlap, dim, paramsNoOverlap, aliases) + if classTypeOverlap is not None: + self.overlappingMeshFactory.register(name, classTypeOverlap, dim, paramsOverlap, aliases) + super(nonlocalMeshFactoryClass, self).register(name, indicators) + + def build(self, name, kernel, boundaryCondition, noRef=0, useMulti=False, **kwargs): + skipMesh = False + if 'skipMesh' in kwargs: + skipMesh = kwargs.pop('skipMesh') + + if kernel is None: + horizonValue = 0. + elif isinstance(kernel.horizon, constant): + horizonValue = kernel.horizon.value + else: + horizonValue = kernel.max_horizon + + domainIndicator, boundaryIndicator, interactionIndicator = super(nonlocalMeshFactoryClass, self).build(name, **kwargs) + + if boundaryCondition == HOMOGENEOUS_DIRICHLET: + if horizonValue == np.inf: + # if kernel.s.max < 0.5: + # tag = NO_BOUNDARY + # else: + # tag = PHYSICAL + tag = PHYSICAL + zeroExterior = True + else: + tag = domainIndicator + zeroExterior = False + hasInteractionDomain = 0 < horizonValue < np.inf + elif boundaryCondition == HOMOGENEOUS_NEUMANN: + tag = NO_BOUNDARY + zeroExterior = False + hasInteractionDomain = False + elif boundaryCondition == DIRICHLET: + if horizonValue == np.inf: + if kernel.s.max < 0.5: + tag = NO_BOUNDARY + else: + tag = PHYSICAL + raise NotImplementedError("Non-homogeneous Dirichlet conditions for infinite horizon kernels are not implemented.") + else: + tag = NO_BOUNDARY + zeroExterior = False + hasInteractionDomain = 0 < horizonValue < np.inf + elif boundaryCondition == NEUMANN: + if horizonValue == np.inf: + assert False + else: + tag = NO_BOUNDARY + zeroExterior = False + hasInteractionDomain = True + elif boundaryCondition == NORM: + tag = PHYSICAL + zeroExterior = kernel.s.max >= 0.5 + hasInteractionDomain = False + else: + raise NotImplementedError('Unknown boundary condition {}'.format(boundaryCondition)) + + if not skipMesh: + if hasInteractionDomain: + assert 0 < horizonValue < np.inf, horizonValue + kwargs['horizon'] = horizonValue + mesh = self.overlappingMeshFactory.build(name, noRef, **kwargs) + else: + mesh = self.nonOverlappingMeshFactory.build(name, noRef, **kwargs) + + dmTest = P1_DoFMap(mesh, tag) + while dmTest.num_dofs == 0: + mesh = mesh.refine() + dmTest = P1_DoFMap(mesh, tag) + + nonlocalInfo = {'domain': domainIndicator, + 'boundary': boundaryIndicator, + 'interaction': interactionIndicator, + 'tag': tag, + 'zeroExterior': zeroExterior} + if not skipMesh: + return mesh, nonlocalInfo + else: + return nonlocalInfo + + def getDim(self, name): + return self.nonOverlappingMeshFactory.getDim(name) + + +def intervalIndicators(a=-1, b=1, **kwargs): + eps = 1e-12 + domainIndicator = squareIndicator(np.array([a+eps], dtype=REAL), + np.array([b-eps], dtype=REAL)) + interactionIndicator = Lambda(lambda x: 1. if ((x[0] < a-eps) or (b+eps < x[0])) else 0.) + boundaryIndicator = Lambda(lambda x: 1. if ((a-eps < x[0] < a+eps) or (b-eps < x[0] < b+eps)) else 0.) + return domainIndicator, boundaryIndicator, interactionIndicator + + +def squareIndicators(ax=-1., ay=-1., bx=1., by=1., **kwargs): + eps = 1e-12 + domainIndicator = squareIndicator(np.array([ax+eps, ay+eps], dtype=REAL), + np.array([bx-eps, by-eps], dtype=REAL)) + interactionIndicator = constant(1.)-squareIndicator(np.array([ax-eps, ay-eps], dtype=REAL), + np.array([bx+eps, by+eps], dtype=REAL)) + boundaryIndicator = constant(1.)-domainIndicator-interactionIndicator + return domainIndicator, boundaryIndicator, interactionIndicator + + +def radialIndicators(*args, **kwargs): + eps = 1e-12 + domainIndicator = radialIndicator(1.-eps) + interactionIndicator = constant(1.)-radialIndicator(1.+eps) + boundaryIndicator = radialIndicator(1.+eps)-radialIndicator(1.-eps) + return domainIndicator, boundaryIndicator, interactionIndicator + + +def twinDiscIndicators(radius=1., sep=0.1, **kwargs): + eps = 1e-9 + domainIndicator = (radialIndicator(radius-eps, np.array([sep/2+radius, 0.], dtype=REAL)) + + radialIndicator(radius-eps, np.array([-sep/2-radius, 0.], dtype=REAL))) + interactionIndicator = constant(1.)-(radialIndicator(radius+eps, np.array([sep/2+radius, 0.], dtype=REAL)) + + radialIndicator(radius+eps, np.array([-sep/2-radius, 0.], dtype=REAL))) + boundaryIndicator = ((radialIndicator(radius+eps, np.array([sep/2+radius, 0.], dtype=REAL)) + + radialIndicator(radius+eps, np.array([-sep/2-radius, 0.], dtype=REAL))) - + (radialIndicator(radius-eps, np.array([sep/2+radius, 0.], dtype=REAL)) + + radialIndicator(radius-eps, np.array([-sep/2-radius, 0.], dtype=REAL)))) + return domainIndicator, boundaryIndicator, interactionIndicator + + +def boxIndicators(ax=-1., ay=-1., az=-1., bx=1., by=1., bz=1., **kwargs): + eps = 1e-9 + domainIndicator = squareIndicator(np.array([ax+eps, ay+eps, az+eps], dtype=REAL), + np.array([bx-eps, by-eps, bz-eps], dtype=REAL)) + interactionIndicator = constant(1.)-squareIndicator(np.array([ax-eps, ay-eps, az-eps], dtype=REAL), + np.array([bx+eps, by+eps, bz+eps], dtype=REAL)) + boundaryIndicator = constant(1.)-domainIndicator-interactionIndicator + return domainIndicator, boundaryIndicator, interactionIndicator + + +def ballWithInteractions(*args, **kwargs): + radius = kwargs.get('radius') + horizon = kwargs.get('horizon') + kwargs['radius'] = radius+horizon + return ball(**kwargs) + + +nonlocalMeshFactory = nonlocalMeshFactoryClass() +nonlocalMeshFactory.register('interval', simpleInterval, intervalWithInteraction, 1, intervalIndicators, + {'a': -1, 'b': 1}, {'a': -1, 'b': 1}) +nonlocalMeshFactory.register('gradedInterval', graded_interval, double_graded_interval_with_interaction, 1, intervalIndicators, + {'a': -1, 'b': 1, 'mu': 2., 'mu2': 2.}, {'a': -1, 'b': 1, 'mu_ll': 2., 'mu_rr': 2.}) +nonlocalMeshFactory.register('square', uniformSquare, squareWithInteractions, 2, squareIndicators, + {'N': 2, 'M': 2, 'ax': -1, 'ay': -1, 'bx': 1, 'by': 1}, {'ax': -1, 'ay': -1, 'bx': 1, 'by': 1}, aliases=['rectangle']) +nonlocalMeshFactory.register('disc', discWithInteraction, discWithInteraction, 2, radialIndicators, + {'horizon': 0., 'radius': 1.}, {'radius': 1.}) +nonlocalMeshFactory.register('gradedDisc', gradedDiscWithInteraction, gradedDiscWithInteraction, 2, radialIndicators, + {'horizon': 0., 'radius': 1.}, {'radius': 1.}) +nonlocalMeshFactory.register('discWithIslands', discWithIslands, discWithIslands, 2, radialIndicators, + {'horizon': 0., 'radius': 1., 'islandOffCenter': 0.35, 'islandDiam': 0.5}, + {'radius': 1., 'islandOffCenter': 0.35, 'islandDiam': 0.5}) +nonlocalMeshFactory.register('twinDisc', twinDisc, twinDisc, 2, radialIndicators, + {'radius': 1., 'sep': 0.1}, {'radius': 1., 'sep': 0.1}) +# nonlocalMeshFactory.register('box', box, boxWithInteractions, 3, boxIndicators, +# {'Nx': 2, 'Ny': 2, 'Nz': 2, 'ax': -1, 'ay': -1, 'az': -1, 'bx': 1, 'by': 1, 'bz': 1}, +# {'Nx': 2, 'Ny': 2, 'Nz': 2, 'ax': -1, 'ay': -1, 'az': -1, 'bx': 1, 'by': 1, 'bz': 1}) +nonlocalMeshFactory.register('ball', ball, ballWithInteractions, 3, radialIndicators, + {'radius': 1.}, {'radius': 1.}) + +functionFactory.register('solFractional', solFractional) +functionFactory.register('solFractional1D', solFractional1D) +functionFactory.register('solFractional2D', solFractional2D) +functionFactory.register('rhsFractional1D', rhsFractional1D) +functionFactory.register('rhsFractional2D', rhsFractional2D) +functionFactory.register('solutionArbitraryOrder', solutionArbitraryOrder) +functionFactory.register('rhsArbitraryOrder', rhsArbitraryOrder) diff --git a/nl/PyNucleus_nl/fractionalKernel.pxi b/nl/PyNucleus_nl/fractionalKernel.pxi new file mode 100644 index 00000000..7c69ab49 --- /dev/null +++ b/nl/PyNucleus_nl/fractionalKernel.pxi @@ -0,0 +1,676 @@ +################################################################################### +# Copyright 2021 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. # +# If you want to use this code, please refer to the README.rst and LICENSE files. # +################################################################################### + +cdef class FractionalKernel(Kernel): + """A kernel function that can be used to define a fractional operator.""" + + def __init__(self, + INDEX_t dim, + fractionalOrderBase s, + function horizon, + interactionDomain interaction, + twoPointFunction scaling, + twoPointFunction phi=None, + BOOL_t piecewise=True, + BOOL_t boundary=False, + INDEX_t derivative=0, + REAL_t tempered=0., + REAL_t max_horizon=np.nan, + kernelType kType=FRACTIONAL, + INDEX_t manifold_dim=-1, + INDEX_t termNo=-1): + if derivative == 0: + valueSize = 1 + elif derivative == 1: + valueSize = s.numParameters + self.tempVec = uninitialized((s.numParameters), dtype=REAL) + elif derivative == 2: + valueSize = s.numParameters**2 + self.tempVec = uninitialized((s.numParameters), dtype=REAL) + else: + valueSize = 1 + self.termNo = termNo + assert isinstance(scaling, (constantFractionalLaplacianScaling, variableFractionalLaplacianScaling, variableFractionalLaplacianScalingWithDifferentHorizon)), scaling + assert scaling.termNo == self.termNo, (scaling.termNo, self.termNo) + + super(FractionalKernel, self).__init__(dim, kType, horizon, interaction, scaling, phi, piecewise, boundary, valueSize, max_horizon, manifold_dim=manifold_dim) + + self.symmetric = s.symmetric and scaling.symmetric and interaction.symmetric and (phi is None or phi.symmetric) + self.derivative = derivative + self.temperedValue = tempered + + self.s = s + self.variableSingularity = isinstance(self.s, variableFractionalOrder) + + self.min_singularity = self.boundary+self.manifold-self.dim-2*self.s.min + self.max_singularity = self.boundary+self.manifold-self.dim-2*self.s.max + + if self.variableSingularity: + ((self.c_kernel_params+fORDERFUN))[0] = s + self.sValue = np.nan + self.singularityValue = np.nan + else: + self.sValue = self.s.value + self.singularityValue = self.max_singularity + setREAL(self.c_kernel_params, fEXPONENT, self.max_singularity) + + self.variable = self.variableSingularity or self.variableHorizon or self.variableScaling + + if piecewise and isinstance(s, singleVariableUnsymmetricFractionalOrder): + self.piecewise = False + + if tempered == 0.: + if derivative == 0: + self.setKernelFun(monomialKernel) + elif derivative == 1: + if termNo == 1: + self.min_log_singularity = 1. + self.max_log_singularity = 1. + self.logSingularityValue = 1. + self.setKernelFun(monomialLogKernel) + elif termNo == 0 or termNo == -1: + self.setKernelFun(monomialKernel) + else: + raise NotImplementedError() + elif derivative == 2: + if termNo == 2: + self.min_log_singularity = 2. + self.max_log_singularity = 2. + self.logSingularityValue = 2. + self.setKernelFun(monomialLogKernel) + elif termNo == 1: + self.min_log_singularity = 1. + self.max_log_singularity = 1. + self.logSingularityValue = 1. + self.setKernelFun(monomialLogKernel) + elif termNo == 0 or termNo == -1: + self.setKernelFun(monomialKernel) + else: + raise NotImplementedError() + else: + raise NotImplementedError() + else: + if not boundary: + self.setKernelFun(temperedMonomialKernel) + else: + self.setKernelFun(temperedMonomialKernelBoundary) + + @staticmethod + def build(dim, + s, + horizon=None, + interaction=None, + twoPointFunction scaling=None, + BOOL_t normalized=True, + BOOL_t piecewise=True, + twoPointFunction phi=None, + INDEX_t derivative=0, + REAL_t tempered=0., + REAL_t max_horizon=np.nan, + BOOL_t manifold=False, + INDEX_t termNo=0): + dim_ = _getDim(dim) + sFun = _getFractionalOrder(s) + horizonFun = _getHorizon(horizon) + interaction = _getInteraction(interaction, horizonFun) + + if isinstance(sFun, admissibleSet): + kernel = RangedFractionalKernel(dim=dim_, admissibleOrders=sFun, horizon=horizonFun, normalized=normalized, tempered=tempered) + else: + if scaling is None: + if isinstance(sFun, constFractionalOrder) and isinstance(horizonFun, constant): + scaling = constantFractionalLaplacianScaling(dim, sFun.value, horizonFun.value, normalized, False, derivative, tempered, termNo=termNo) + else: + symmetric = sFun.symmetric and isinstance(horizonFun, constant) + if piecewise and isinstance(sFun, singleVariableUnsymmetricFractionalOrder): + piecewise = False + scaling = variableFractionalLaplacianScaling(symmetric, normalized, False, derivative, termNo) + kernel = FractionalKernel(dim=dim_, s=sFun, horizon=horizonFun, interaction=interaction, scaling=scaling, phi=phi, piecewise=piecewise, boundary=False, + derivative=derivative, tempered=tempered, max_horizon=max_horizon, termNo=termNo) + + from . twoPointFunctions import parametrizedTwoPointFunction + if isinstance(kernel.scaling, parametrizedTwoPointFunction): + assert kernel.getParamPtrAddr() == kernel.scaling.getParamPtrAddr() + if isinstance(kernel.interaction, parametrizedTwoPointFunction): + assert kernel.getParamPtrAddr() == kernel.interaction.getParamPtrAddr() + return kernel + + cdef setKernelFun(self, kernel_fun_t kernelFun): + self.kernelFun = kernelFun + + cdef void setSingularityValue(self, REAL_t singularity): + setREAL(self.c_kernel_params, fSINGULARITY, singularity) + setREAL(self.c_kernel_params, fEXPONENT, singularity) + + @property + def variableOrder(self): + return self.variableSingularity + + @property + def sValue(self): + "The value of the fractional order" + return getREAL(self.c_kernel_params, fS) + + @sValue.setter + def sValue(self, REAL_t s): + setREAL(self.c_kernel_params, fS, s) + + cdef REAL_t getsValue(self): + return getREAL(self.c_kernel_params, fS) + + cdef void setsValue(self, REAL_t s): + setREAL(self.c_kernel_params, fS, s) + + @property + def temperedValue(self): + "The value of the tempering parameter" + return getREAL(self.c_kernel_params, fTEMPERED) + + @temperedValue.setter + def temperedValue(self, REAL_t tempered): + setREAL(self.c_kernel_params, fTEMPERED, tempered) + + cdef REAL_t gettemperedValue(self): + return getREAL(self.c_kernel_params, fTEMPERED) + + cdef void settemperedValue(self, REAL_t tempered): + setREAL(self.c_kernel_params, fTEMPERED, tempered) + + cdef void evalParamsOnSimplicesPtr(self, INDEX_t dim, REAL_t* center1, REAL_t* center2, REAL_t* simplex1, REAL_t* simplex2): + # Set the max singularity and the horizon. + cdef: + REAL_t sValue, sValue2 + REAL_t[::1] center1A + if self.variableOrder: + if self.s.symmetric: + self.s.evalPtr(dim, center1, center2, &sValue) + else: + sValue = 0. + self.s.evalPtr(dim, center1, center2, &sValue2) + sValue = max(sValue, sValue2) + self.s.evalPtr(dim, center2, center1, &sValue2) + sValue = max(sValue, sValue2) + for i in range(self.manifold_dim+1): + self.s.evalPtr(dim, &simplex1[i*self.dim], center2, &sValue2) + sValue = max(sValue, sValue2) + for i in range(self.manifold_dim+1-self.getBoundary()): + self.s.evalPtr(dim, &simplex2[i*self.dim], center1, &sValue2) + sValue = max(sValue, sValue2) + if not self.getBoundary(): + self.setSingularityValue(-self.dim-2*sValue) + else: + self.setSingularityValue(1-self.dim-2*sValue) + if self.variableHorizon: + center1A = center1 + self.setHorizonValue(self.horizon.eval(center1A)) + + cdef void updateParams(self, INDEX_t dim, REAL_t* x, REAL_t* y): + cdef: + REAL_t[::1] xA + REAL_t sValue, scalingValue + if self.variableOrder: + self.s.evalPtr(dim, x, y, &sValue) + if not self.getBoundary(): + self.setSingularityValue(-self.dim-2*sValue) + else: + self.setSingularityValue(1-self.dim-2*sValue) + self.setsValue(sValue) + if self.variableHorizon: + xA = x + self.setHorizonValue(self.horizon.eval(xA)) + if self.variableScaling: + self.scaling.evalPtr(dim, x, y, &scalingValue) + self.setScalingValue(scalingValue) + + cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* vec): + cdef: + INDEX_t i, j, k + REAL_t fac + REAL_t[::1] xA + REAL_t sValue, scalingValue + if not self.piecewise: + self.updateParams(dim, x, y) + if self.derivative == 0: + vec[0] = self.kernelFun(x, y, self.c_kernel_params) + elif self.derivative == 1: + fac = self.kernelFun(x, y, self.c_kernel_params) + self.s.evalGradPtr(dim, x, y, self.valueSize, vec) + for i in range(self.valueSize): + vec[i] *= fac + elif self.derivative == 2: + fac = self.kernelFun(&x[0], &y[0], self.c_kernel_params) + self.s.evalGradPtr(dim, x, y, self.s.numParameters, &self.tempVec[0]) + k = 0 + for i in range(self.s.numParameters): + for j in range(self.s.numParameters): + vec[k] = fac*self.tempVec[i]*self.tempVec[j] + k += 1 + + def getModifiedKernel(self, + fractionalOrderBase s=None, + function horizon=None, + interactionDomain interaction=None, + twoPointFunction scaling=None, + twoPointFunction phi=None): + if s is None: + s = self.s + else: + if scaling is None and isinstance(self.scalingPrePhi, variableFractionalLaplacianScaling): + raise NotImplementedError() + if interaction is None: + if horizon is None: + horizon = self.horizon + interaction = self.interaction + else: + if scaling is None and isinstance(self.scalingPrePhi, variableFractionalLaplacianScaling): + scaling = self.scalingPrePhi.getScalingWithDifferentHorizon() + interaction = deepcopy(self.interaction) + else: + assert horizon is None + if phi is None: + phi = deepcopy(self.phi) + if scaling is None: + scaling = deepcopy(self.scalingPrePhi) + newKernel = FractionalKernel.build(dim=self.dim, s=s, horizon=horizon, interaction=interaction, scaling=scaling, phi=phi, piecewise=self.piecewise, derivative=self.derivative, tempered=self.temperedValue, termNo=self.termNo) + self._checkNewKernel(newKernel) + return newKernel + + def getBoundaryKernel(self, REAL_t[::1] interpolation_nodes=None, str kind='linear'): + "Get the boundary kernel. This is the kernel that corresponds to the elimination of a subdomain via Gauss theorem." + cdef: + constantFractionalLaplacianScaling scal + variableFractionalLaplacianScaling scalVar + variableFractionalLaplacianScalingWithDifferentHorizon scalVarDiffHorizon + + assert not self.boundary + if interpolation_nodes is None: + s = deepcopy(self.s) + + if isinstance(self.scalingPrePhi, constantFractionalLaplacianScaling): + scal = self.scalingPrePhi + scaling = constantFractionalLaplacianScaling(dim=scal.dim, s=scal.s, horizon=scal.horizon, normalized=scal.normalized, boundary=True, derivative=scal.derivative, tempered=scal.tempered, termNo=scal.termNo) + elif isinstance(self.scalingPrePhi, variableFractionalLaplacianScalingWithDifferentHorizon): + scalVarDiffHorizon = self.scalingPrePhi + scaling = variableFractionalLaplacianScalingWithDifferentHorizon(symmetric=scalVarDiffHorizon.symmetric, normalized=scalVarDiffHorizon.normalized, boundary=True, derivative=scalVarDiffHorizon.derivative, termNo=scalVarDiffHorizon.termNo, horizonFun=scalVarDiffHorizon.horizonFun) + elif isinstance(self.scalingPrePhi, variableFractionalLaplacianScaling): + scalVar = self.scalingPrePhi + scaling = variableFractionalLaplacianScaling(symmetric=scalVar.symmetric, normalized=scalVar.normalized, boundary=True, derivative=scalVar.derivative, termNo=scalVar.termNo) + else: + raise NotImplementedError(type(self.scalingPrePhi)) + + newKernel = FractionalBoundaryKernel.build(dim=self.dim, + s=s, + horizon=deepcopy(self.horizon), + interaction=None, + scaling=scaling, + phi=deepcopy(self.phi), + piecewise=self.piecewise, + derivative=self.derivative, + tempered=self.temperedValue, + termNo=self.termNo) + else: + newKernel = super().getBoundaryKernel(interpolation_nodes, kind) + self._checkNewKernel(newKernel) + return newKernel + + def getComplementKernel(self): + newKernel = FractionalKernel.build(dim=self.dim, s=self.s, horizon=self.horizon, interaction=self.interaction.getComplement(), scaling=self.scalingPrePhi, phi=self.phi, piecewise=self.piecewise, derivative=self.derivative, tempered=self.temperedValue, termNo=self.termNo) + self._checkNewKernel(newKernel) + return newKernel + + def getDerivative(self, INDEX_t derivative): + cdef: + constantFractionalLaplacianScaling scal + variableFractionalLaplacianScaling scalVar + list terms = [] + INDEX_t termNo + assert not self.boundary + for termNo in range(derivative+1): + interaction = deepcopy(self.interaction) + if isinstance(self.scaling, constantFractionalLaplacianScaling): + scal = self.scaling + scaling = constantFractionalLaplacianScaling(dim=scal.dim, s=scal.s, horizon=scal.horizon, normalized=scal.normalized, boundary=scal.boundary, derivative=derivative, tempered=scal.tempered, termNo=termNo) + elif isinstance(self.scaling, variableFractionalLaplacianScaling): + scalVar = self.scaling + scaling = variableFractionalLaplacianScaling(symmetric=scalVar.symmetric, normalized=scalVar.normalized, boundary=scalVar.boundary, derivative=derivative, termNo=termNo) + else: + raise NotImplementedError(type(self.scaling)) + terms.append(FractionalKernel(dim=self.dim, s=self.s, horizon=self.horizon, interaction=interaction, scaling=scaling, phi=self.phi, piecewise=False, boundary=self.boundary, derivative=derivative, tempered=self.temperedValue, termNo=termNo)) + self._checkNewKernel(terms[termNo]) + return MultiSingularityFractionalKernel(terms) + + def getDerivativeKernel(self): + return self.getDerivative(1) + + def getGradientKernel(self): + return self.getDerivative(1) + + def getHessianKernel(self): + return self.getDerivative(2) + + def __repr__(self): + if self.temperedValue != 0.: + return "{}({}, {}, {}, tempered {})".format(self.__class__.__name__, self.s, repr(self.interaction), self.scaling, self.temperedValue) + else: + if self.logSingularityValue == 0.: + return "{}({}, {}, {})".format(self.__class__.__name__, self.s, repr(self.interaction), self.scaling) + else: + return "{}({}, logSingularity={}, {}, {})".format(self.__class__.__name__, self.s, self.logSingularityValue, repr(self.interaction), self.scaling) + + def getLongDescription(self): + cdef: + str descr = '' + dict params = {} + params['d'] = self.dim + if self.temperedValue != 0.: + descr = 'MISSING_DESCRIPTION' + else: + if self.logSingularityValue == 0.: + descr = '\\frac{1}{|x-y|^{d+2s}}' + else: + descr = '\\frac{(-log(|x-y|))^{\\beta}}{|x-y|^{d+2s}}' + params['\\beta'] = self.logSingularityValue + paramsStr = ', '.join([k+'='+str(v) for k, v in params.items()]) + return self.scaling.getLongDescription() + ' ' + descr + ' ' + self.interaction.getLongDescription() + ', ' + paramsStr + + def __reduce__(self): + return FractionalKernel, (self.dim, self.s, self.horizon, self.interaction, self.scalingPrePhi, self.phi, self.piecewise, self.boundary, self.derivative, self.temperedValue, self.max_horizon, self.kernelType, self.manifold_dim, self.termNo) + + def __eq__(self, FractionalKernel other): + if other is None: + return False + return (self.dim == other.dim) and (self.s == other.s) and (self.horizon == other.horizon) and (self.interaction == other.interaction) and (self.scalingPrePhi == other.scalingPrePhi) and (self.phi == other.phi) and (self.piecewise == other.piecewise) and (self.boundary == other.boundary) and (self.derivative == other.derivative) and (self.temperedValue == other.temperedValue) and (self.max_horizon == other.max_horizon) + + +cdef class FractionalBoundaryKernel(FractionalKernel): + def __init__(self, + INDEX_t dim, + fractionalOrderBase s, + function horizon, + interactionDomain interaction, + twoPointFunction scaling, + twoPointFunction phi=None, + BOOL_t piecewise=True, + INDEX_t derivative=0, + REAL_t tempered=0., + REAL_t max_horizon=np.nan, + INDEX_t manifold_dim=-1, + INDEX_t termNo=-1): + super(FractionalBoundaryKernel, self).__init__(dim=dim, + s=s, + horizon=horizon, + interaction=interaction, + scaling=scaling, + phi=phi, + piecewise=piecewise, + boundary=True, + derivative=derivative, + tempered=tempered, + max_horizon=max_horizon, + manifold_dim=manifold_dim, + termNo=termNo) + if tempered == 0.: + if derivative == 0: + self.setKernelFun(monomialKernel) + elif derivative == 1: + if termNo == 1: + self.min_log_singularity = 1. + self.max_log_singularity = 1. + self.logSingularityValue = 1. + self.setKernelFun(monomialLogKernel) + elif termNo == 0 or termNo == -1: + self.setKernelFun(monomialKernel) + else: + raise NotImplementedError() + elif derivative == 2: + if termNo == 2: + self.min_log_singularity = 2. + self.max_log_singularity = 2. + self.logSingularityValue = 2. + self.setKernelFun(monomialLogKernel) + elif termNo == 1: + self.min_log_singularity = 1. + self.max_log_singularity = 1. + self.logSingularityValue = 1. + self.setKernelFun(monomialLogKernel) + elif termNo == 0 or termNo == -1: + self.setKernelFun(monomialKernel) + else: + raise NotImplementedError() + else: + raise NotImplementedError() + else: + self.setKernelFun(temperedMonomialKernelBoundary) + self.isSphericalManifold = False + self.n = uninitialized((self.dim), dtype=REAL) + + cdef void setSimplices(self, REAL_t[:, ::1] simplex1, REAL_t[:, ::1] simplex2): + cdef: + REAL_t val + if self.isSphericalManifold: + self.n[0] = 1. + elif self.dim == 1: + if 0.5*(simplex1[0, 0]+simplex1[1, 0]) > simplex2[0, 0]: + self.n[0] = -1. + else: + self.n[0] = 1. + elif self.dim == 2: + # (x_1-x_0)^\perp + self.n[0] = simplex2[1, 1] - simplex2[0, 1] + self.n[1] = simplex2[0, 0] - simplex2[1, 0] + val = 1./sqrt(mydot(self.n, self.n)) + self.n[0] *= val + self.n[1] *= val + elif self.dim == 3: + self.n[0] = (simplex2[1, 1]-simplex2[0, 1])*(simplex2[2, 2]-simplex2[0, 2])-(simplex2[1, 2]-simplex2[0, 2])*(simplex2[2, 1]-simplex2[0, 1]) + self.n[1] = (simplex2[1, 2]-simplex2[0, 2])*(simplex2[2, 0]-simplex2[0, 0])-(simplex2[1, 0]-simplex2[0, 0])*(simplex2[2, 2]-simplex2[0, 2]) + self.n[2] = (simplex2[1, 0]-simplex2[0, 0])*(simplex2[2, 1]-simplex2[0, 1])-(simplex2[1, 1]-simplex2[0, 1])*(simplex2[2, 0]-simplex2[0, 0]) + val = 1./sqrt(mydot(self.n, self.n)) + self.n[0] *= val + self.n[1] *= val + self.n[2] *= val + + def __reduce__(self): + return FractionalBoundaryKernel, (self.dim, self.s, self.horizon, self.interaction, self.scalingPrePhi, self.phi, self.piecewise, self.derivative, self.temperedValue, self.max_horizon, self.manifold_dim, self.termNo) + + @staticmethod + def build(dim, + s, + horizon=None, + interaction=None, + twoPointFunction scaling=None, + BOOL_t normalized=True, + BOOL_t piecewise=True, + twoPointFunction phi=None, + INDEX_t derivative=0, + REAL_t tempered=0., + REAL_t max_horizon=np.nan, + BOOL_t manifold=False, + INDEX_t termNo=-1): + dim_ = _getDim(dim) + sFun = _getFractionalOrder(s) + horizonFun = _getHorizon(horizon) + interaction = _getInteraction(interaction, horizonFun) + + if isinstance(sFun, admissibleSet): + kernel = RangedFractionalKernel(dim=dim_, admissibleOrders=sFun, horizon=horizonFun, normalized=normalized, tempered=tempered) + else: + if scaling is None: + if isinstance(sFun, constFractionalOrder) and isinstance(horizonFun, constant): + scaling = constantFractionalLaplacianScaling(dim=dim-manifold, s=sFun.value, horizon=horizonFun.value, normalized=normalized, boundary=True, derivative=derivative, tempered=tempered, termNo=termNo) + else: + symmetric = sFun.symmetric and isinstance(horizonFun, constant) + if piecewise and isinstance(sFun, singleVariableUnsymmetricFractionalOrder): + piecewise = False + scaling = variableFractionalLaplacianScaling(symmetric, normalized, True, derivative) + kernel = FractionalBoundaryKernel(dim_, sFun, horizonFun, interaction, scaling, phi=phi, piecewise=piecewise, + derivative=derivative, tempered=tempered, max_horizon=max_horizon, termNo=termNo) + + from . twoPointFunctions import parametrizedTwoPointFunction + if isinstance(kernel.scaling, parametrizedTwoPointFunction): + assert kernel.getParamPtrAddr() == kernel.scaling.getParamPtrAddr() + if isinstance(kernel.interaction, parametrizedTwoPointFunction): + assert kernel.getParamPtrAddr() == kernel.interaction.getParamPtrAddr() + return kernel + + cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* vec): + cdef: + REAL_t temp + INDEX_t i, j, m, k + if not self.piecewise: + self.updateParams(dim, x, y) + temp = self.kernelFun(x, y, self.c_kernel_params) + temp /= sqrt(self.interaction.dist2) + if self.derivative == 0: + vec[0] = 0. + for i in range(self.dim): + vec[0] += temp * (x[i]-y[i])*self.n[i] + elif self.derivative == 1: + self.s.evalGradPtr(dim, x, y, self.s.numParameters, &vec[0]) + temp2 = 0. + for j in range(self.dim): + temp2 += (x[j]-y[j]) * self.n[j] + for i in range(self.s.numParameters): + vec[i] *= temp * temp2 + elif self.derivative == 2: + self.s.evalGradPtr(dim, x, y, self.s.numParameters, &self.tempVec[0]) + temp2 = 0. + for j in range(self.dim): + temp2 += (x[j]-y[j]) * self.n[j] + k = 0 + for i in range(self.s.numParameters): + for m in range(self.s.numParameters): + vec[k] = temp*self.tempVec[i]*self.tempVec[m] * temp2 + k += 1 + + def getBoundaryKernel(self, REAL_t[::1] interpolation_nodes=None, str kind='linear'): + raise NotImplementedError() + + +cdef class RangedFractionalKernel(FractionalKernel): + def __init__(self, + INDEX_t dim, + admissibleOrders, + function horizon, + BOOL_t normalized=True, + REAL_t tempered=0., + REAL_t errorBound=-1., + INDEX_t M_min=1, INDEX_t M_max=20, + REAL_t xi=0.): + self.dim = dim + assert admissibleOrders.numParams == 1, "Cannot handle {} params".format(admissibleOrders.numParams) + self.admissibleOrders = admissibleOrders + assert isinstance(horizon, constant) + self.horizon = horizon + if isinstance(horizon, constant) and horizon.value == np.inf: + self.interaction = fullSpace() + else: + self.interaction = ball2_retriangulation() + self.normalized = normalized + self.tempered = tempered + + self.setOrder(admissibleOrders.getLowerBounds()[0]) + + self.errorBound = errorBound + self.M_min = M_min + self.M_max = M_max + self.xi = xi + + def setOrder(self, REAL_t s): + assert self.admissibleOrders.isAdmissible(s) + sFun = constFractionalOrder(s) + dim = self.dim + horizon = self.horizon + interactionDomain = self.interaction + tempered = self.tempered + scaling = constantFractionalLaplacianScaling(dim=dim, s=sFun.value, horizon=horizon.value, normalized=self.normalized, boundary=False, derivative=0, tempered=tempered, termNo=0) + super(RangedFractionalKernel, self).__init__(dim, sFun, horizon, interactionDomain, scaling, tempered=tempered, termNo=0) + + def getFrozenKernel(self, REAL_t s): + assert self.admissibleOrders.isAdmissible(s) + sFun = constFractionalOrder(s) + dim = self.dim + horizon = self.horizon + tempered = self.tempered + interactionDomain = deepcopy(self.interaction) + scaling = constantFractionalLaplacianScaling(dim=dim, s=sFun.value, horizon=horizon.value, normalized=self.normalized, boundary=False, derivative=0, tempered=tempered, termNo=0) + return FractionalKernel(dim, sFun, horizon, interactionDomain, scaling, termNo=scaling.termNo) + + def __repr__(self): + return 'ranged '+super(RangedFractionalKernel, self).__repr__() + + def __reduce__(self): + return RangedFractionalKernel, (self.dim, self.admissibleOrders, self.horizon, self.normalized, self.tempered, self.errorBound, self.M_min, self.M_max, self.xi) + + +cdef class RangedVariableFractionalKernel(FractionalKernel): + def __init__(self, + INDEX_t dim, + function blockIndicator, + admissibleOrders, + function horizon, + BOOL_t normalized=True, + REAL_t errorBound=-1., + INDEX_t M_min=1, INDEX_t M_max=20, + REAL_t xi=0.): + self.dim = dim + self.blockIndicator = blockIndicator + self.admissibleOrders = admissibleOrders + + assert isinstance(horizon, constant) + self.horizon = horizon + if isinstance(horizon, constant) and horizon.value == np.inf: + self.interaction = fullSpace() + else: + self.interaction = ball2_retriangulation() + self.normalized = normalized + + numBlocks = np.around(sqrt(admissibleOrders.numParams)) + assert numBlocks*numBlocks == admissibleOrders.numParams + self.setOrder(admissibleOrders.getLowerBounds().reshape((numBlocks, numBlocks))) + + self.errorBound = errorBound + self.M_min = M_min + self.M_max = M_max + self.xi = xi + + def setOrder(self, REAL_t[:, ::1] sVals): + assert self.admissibleOrders.isAdmissible(np.array(sVals, copy=False).flatten()) + sFun = piecewiseConstantFractionalOrder(self.dim, self.blockIndicator, sVals) + dim = self.dim + horizon = self.horizon + interactionDomain = self.interaction + if self.normalized: + scaling = variableFractionalLaplacianScaling(sFun.symmetric) + else: + scaling = constantTwoPoint(1.0) + super(RangedVariableFractionalKernel, self).__init__(dim, sFun, horizon, interactionDomain, scaling) + + def getFrozenKernel(self, REAL_t[:, ::1] sVals): + assert self.admissibleOrders.isAdmissible(np.array(sVals, copy=False).flatten()) + sFun = piecewiseConstantFractionalOrder(self.dim, self.blockIndicator, sVals) + dim = self.dim + horizon = self.horizon + interactionDomain = self.interaction + if self.normalized: + scaling = variableFractionalLaplacianScaling(sFun.symmetric) + else: + scaling = constantTwoPoint(1.0) + return FractionalKernel(dim, sFun, horizon, interactionDomain, scaling, termNo=scaling.termNo) + + def __repr__(self): + return 'ranged '+super(RangedVariableFractionalKernel, self).__repr__() + + +cdef class MultiSingularityFractionalKernel(MultiSingularityKernel): + def __init__(self, list kernels): + cdef: + FractionalKernel kernel + super(MultiSingularityFractionalKernel, self).__init__(kernels) + kernel = kernels[0] + self.variableOrder = kernel.variableOrder + self.derivative = kernel.derivative diff --git a/nl/PyNucleus_nl/fractionalKernel_decl.pxi b/nl/PyNucleus_nl/fractionalKernel_decl.pxi new file mode 100644 index 00000000..27c172ce --- /dev/null +++ b/nl/PyNucleus_nl/fractionalKernel_decl.pxi @@ -0,0 +1,28 @@ +################################################################################### +# Copyright 2021 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. # +# If you want to use this code, please refer to the README.rst and LICENSE files. # +################################################################################### + +cdef class FractionalKernel(Kernel): + cdef: + public fractionalOrderBase s + public INDEX_t derivative + public INDEX_t termNo + REAL_t[::1] tempVec + cdef REAL_t getsValue(self) + cdef void setsValue(self, REAL_t s) + cdef REAL_t gettemperedValue(self) + cdef void settemperedValue(self, REAL_t tempered) + + +cdef class FractionalBoundaryKernel(FractionalKernel): + cdef: + public BOOL_t isSphericalManifold + + +cdef class MultiSingularityFractionalKernel(MultiSingularityKernel): + cdef: + public BOOL_t variableOrder + public INDEX_t derivative diff --git a/nl/PyNucleus_nl/fractionalLaplacian1D.pxd b/nl/PyNucleus_nl/fractionalLaplacian1D.pxd index ef6c6405..8e28e7e9 100644 --- a/nl/PyNucleus_nl/fractionalLaplacian1D.pxd +++ b/nl/PyNucleus_nl/fractionalLaplacian1D.pxd @@ -16,36 +16,33 @@ from . nonlocalOperator cimport (double_local_matrix_t, nonlocalLaplacian1D, panelType, MASK_t, + singularityCancelationQuadRule, specialQuadRule) from . fractionalOrders cimport fractionalOrderBase -from . kernelsCy cimport (Kernel, - FractionalKernel) +from . kernels cimport (Kernel, + FractionalKernel) cdef class fractionalLaplacian1DZeroExterior(nonlocalLaplacian1D): cdef: - public quadratureRule qrVertex - public REAL_t[:, ::1] PHI_dist, PHI_sep, PHI_vertex dict distantPHI -cdef class singularityCancelationQuadRule1D(quadratureRule): + +cdef class singularityCancelationQuadRule1D(singularityCancelationQuadRule): pass cdef class fractionalLaplacian1D(nonlocalLaplacian1D): cdef: - public quadratureRule qrId, qrVertex - REAL_t[:, ::1] PSI_id, PSI_vertex REAL_t singularityCancelationIntegrandWithinElement REAL_t singularityCancelationIntegrandAcrossElements cdef class fractionalLaplacian1D_nonsym(fractionalLaplacian1D): - cdef: - REAL_t[:, :, ::1] PHI_id, PHI_vertex + pass +cdef class fractionalLaplacian1D_nonsym2(fractionalLaplacian1D_nonsym): + pass cdef class fractionalLaplacian1D_boundary(fractionalLaplacian1DZeroExterior): pass - - diff --git a/nl/PyNucleus_nl/fractionalLaplacian1D.pyx b/nl/PyNucleus_nl/fractionalLaplacian1D.pyx index 8e3640c8..275e414f 100644 --- a/nl/PyNucleus_nl/fractionalLaplacian1D.pyx +++ b/nl/PyNucleus_nl/fractionalLaplacian1D.pyx @@ -5,14 +5,15 @@ # If you want to use this code, please refer to the README.rst and LICENSE files. # ################################################################################### -from libc.math cimport log, ceil, fabs as abs +from libc.math cimport sqrt, log, ceil, fabs as abs import numpy as np cimport numpy as np from PyNucleus_base.myTypes import REAL from PyNucleus_base import uninitialized +from PyNucleus_base.blas cimport mydot from PyNucleus_fem.meshCy cimport meshBase -from PyNucleus_fem.quadrature cimport GaussJacobi +from PyNucleus_fem.quadrature cimport GaussJacobi, LogGaussJacobi from PyNucleus_fem.DoFMaps cimport DoFMap, P1_DoFMap, P0_DoFMap, shapeFunction include "kernel_params.pxi" @@ -26,14 +27,14 @@ ALL.set() cdef class fractionalLaplacian1DZeroExterior(nonlocalLaplacian1D): def __init__(self, Kernel kernel, meshBase mesh, DoFMap dm, num_dofs=None, **kwargs): - manifold_dim2 = mesh.dim-1 + manifold_dim2 = mesh.manifold_dim-1 super(fractionalLaplacian1DZeroExterior, self).__init__(kernel, mesh, dm, num_dofs, manifold_dim2=manifold_dim2, **kwargs) self.symmetricCells = False self.symmetricLocalMatrix = True -cdef class singularityCancelationQuadRule1D(quadratureRule): - def __init__(self, panelType panel, REAL_t singularity, INDEX_t quad_order_diagonal, INDEX_t quad_order_regular): +cdef class singularityCancelationQuadRule1D(singularityCancelationQuadRule): + def __init__(self, panelType panel, REAL_t singularity, REAL_t log_singularity, INDEX_t quad_order_diagonal, INDEX_t quad_order_regular): cdef: INDEX_t i REAL_t eta0, eta1, x, y @@ -44,18 +45,36 @@ cdef class singularityCancelationQuadRule1D(quadratureRule): REAL_t[:, ::1] bary, bary_x, bary_y REAL_t[::1] weights INDEX_t offset + REAL_t[::1] singularPart if panel == COMMON_EDGE: - # Differences of basis functions one the same element - # cancel one singularity order i.e. - # singularityCancelationFromContinuity = 1. - qrId = GaussJacobi(((quad_order_regular, 1+singularity, 0), - (quad_order_regular, 0+singularity, 0))) + if log_singularity == 0: + qrId = GaussJacobi(((quad_order_regular, 1+singularity, 0), + (quad_order_regular, 0+singularity, 0))) + elif log_singularity == 1: + qr10 = LogGaussJacobi(((quad_order_regular, 1+singularity, 0, 1), + (quad_order_regular, 0+singularity, 0, 0))) + qr01 = LogGaussJacobi(((quad_order_regular, 1+singularity, 0, 0), + (quad_order_regular, 0+singularity, 0, 1))) + qrId = qr10+qr01 + elif log_singularity == 2: + qr20 = LogGaussJacobi(((quad_order_regular, 1+singularity, 0, 2), + (quad_order_regular, 0+singularity, 0, 0))) + qr11 = LogGaussJacobi(((quad_order_regular, 1+singularity, 0, 1), + (quad_order_regular, 0+singularity, 0, 1))) + qr02 = LogGaussJacobi(((quad_order_regular, 1+singularity, 0, 0), + (quad_order_regular, 0+singularity, 0, 2))) + for i in range(qr11.num_nodes): + qr11.weights[i] *= 2. + qrId = qr20+qr11+qr02 + else: + raise NotImplementedError(log_singularity) bary = uninitialized((2*dim+2, qrId.num_nodes), dtype=REAL) bary_x = bary[:dim+1, :] bary_y = bary[dim+1:, :] + singularPart = uninitialized((qrId.num_nodes), dtype=REAL) weights = uninitialized((qrId.num_nodes), dtype=REAL) # COMMON_FACE panels for i in range(qrId.num_nodes): @@ -77,18 +96,24 @@ cdef class singularityCancelationQuadRule1D(quadratureRule): bary_y[0, i] = lcl_bary_y[0] bary_y[1, i] = lcl_bary_y[1] - weights[i] = 2.0*qrId.weights[i]*(eta0*eta1)**(-singularity) + singularPart[i] = eta0*eta1 + weights[i] = 2.0*qrId.weights[i]*singularPart[i]**(-singularity) super(singularityCancelationQuadRule1D, self).__init__(bary, weights, 2*dim+2) elif panel == COMMON_VERTEX: - qrVertex = GaussJacobi(((quad_order_regular, 1+singularity, 0), - (quad_order_diagonal, 0, 0))) + if log_singularity == 0: + qrVertex = GaussJacobi(((quad_order_regular, 1+singularity, 0), + (quad_order_diagonal, 0, 0))) + else: + qrVertex = LogGaussJacobi(((quad_order_regular, 1+singularity, 0, log_singularity), + (quad_order_diagonal, 0, 0, 0))) bary = uninitialized((2*dim+2, 2*qrVertex.num_nodes), dtype=REAL) bary_x = bary[:dim+1, :] bary_y = bary[dim+1:, :] + singularPart = uninitialized((2*qrVertex.num_nodes), dtype=REAL) weights = uninitialized((2*qrVertex.num_nodes), dtype=REAL) # panels with common vertex @@ -113,7 +138,8 @@ cdef class singularityCancelationQuadRule1D(quadratureRule): bary_y[0, offset+i] = lcl_bary_y[0] bary_y[1, offset+i] = lcl_bary_y[1] - weights[offset+i] = qrVertex.weights[i] * eta0**(-singularity) + singularPart[offset+i] = eta0 + weights[offset+i] = qrVertex.weights[i] * singularPart[offset+i]**(-singularity) # second integral offset = qrVertex.num_nodes @@ -136,13 +162,17 @@ cdef class singularityCancelationQuadRule1D(quadratureRule): bary_y[0, offset+i] = lcl_bary_y[0] bary_y[1, offset+i] = lcl_bary_y[1] - weights[offset+i] = qrVertex.weights[i] * eta0**(-singularity) - + singularPart[offset+i] = eta0 + weights[offset+i] = qrVertex.weights[i] * singularPart[offset+i]**(-singularity) super(singularityCancelationQuadRule1D, self).__init__(bary, weights, 2*dim) + else: + raise NotImplementedError(panel) + self.singularPart = singularPart -cdef class singularityCancelationQuadRule1D_boundary(quadratureRule): - def __init__(self, panelType panel, REAL_t singularity, INDEX_t quad_order_diagonal, INDEX_t quad_order_regular): + +cdef class singularityCancelationQuadRule1D_boundary(singularityCancelationQuadRule): + def __init__(self, panelType panel, REAL_t singularity, REAL_t log_singularity, INDEX_t quad_order_diagonal, INDEX_t quad_order_regular): cdef: INDEX_t i REAL_t eta @@ -152,14 +182,19 @@ cdef class singularityCancelationQuadRule1D_boundary(quadratureRule): REAL_t lcl_bary_y[1] REAL_t[:, ::1] bary, bary_x, bary_y REAL_t[::1] weights + REAL_t[::1] singularPart if panel == COMMON_VERTEX: - qrVertex = GaussJacobi(((quad_order_diagonal, singularity, 0), )) + if log_singularity == 0: + qrVertex = GaussJacobi(((quad_order_diagonal, singularity, 0), )) + else: + qrVertex = LogGaussJacobi(((quad_order_diagonal, singularity, 0, log_singularity), )) bary = uninitialized((2*dim+1, qrVertex.num_nodes), dtype=REAL) bary_x = bary[:dim+1, :] bary_y = bary[dim+1:, :] + singularPart = uninitialized((qrVertex.num_nodes), dtype=REAL) weights = uninitialized((qrVertex.num_nodes), dtype=REAL) for i in range(qrVertex.num_nodes): @@ -175,8 +210,10 @@ cdef class singularityCancelationQuadRule1D_boundary(quadratureRule): bary_y[0, i] = lcl_bary_y[0] - weights[i] = qrVertex.weights[i] * eta**(-singularity) + singularPart[i] = eta + weights[i] = qrVertex.weights[i] * singularPart[i]**(-singularity) super(singularityCancelationQuadRule1D_boundary, self).__init__(bary, weights, 2*dim+1) + self.singularPart = singularPart cdef class fractionalLaplacian1D(nonlocalLaplacian1D): @@ -184,7 +221,7 @@ cdef class fractionalLaplacian1D(nonlocalLaplacian1D): .. math:: - \\int_{K_1}\\int_{K_2} (u(x)-u(y)) (v(x)-v(y)) \\gamma(x,y) dy dx + 0.5 \\int_{K_1}\\int_{K_2} (u(x)-u(y)) (v(x)-v(y)) \\gamma(x,y) dy dx for the symmetric 1D nonlocal Laplacian. """ @@ -227,7 +264,7 @@ cdef class fractionalLaplacian1D(nonlocalLaplacian1D): quad_order_diagonal = max(np.ceil(((target_order+2.)*log(self.num_dofs*self.H0) + (2.*smax-1.)*abs(log(self.hmin/self.H0)))/0.8), 2) self.quad_order_diagonal = quad_order_diagonal - if (self.kernel.kernelType != FRACTIONAL) or (not self.kernel.variableOrder): + if (self.kernel.kernelType != FRACTIONAL) or (not self.kernel.variableSingularity): self.getNearQuadRule(COMMON_EDGE) self.getNearQuadRule(COMMON_VERTEX) @@ -256,6 +293,7 @@ cdef class fractionalLaplacian1D(nonlocalLaplacian1D): cdef: INDEX_t i REAL_t singularityValue = self.kernel.getSingularityValue() + REAL_t log_singularityValue = self.kernel.getLogSingularityValue() specialQuadRule sQR quadratureRule qr INDEX_t dofs_per_element = self.DoFMap.dofs_per_element @@ -274,6 +312,7 @@ cdef class fractionalLaplacian1D(nonlocalLaplacian1D): except KeyError: qr = singularityCancelationQuadRule1D(panel, self.singularityCancelationIntegrandWithinElement+singularityValue, + log_singularityValue, self.quad_order_diagonal, 2*dm_order) PSI = uninitialized((dofs_per_element, qr.num_nodes), dtype=REAL) @@ -293,14 +332,15 @@ cdef class fractionalLaplacian1D(nonlocalLaplacian1D): self.specialQuadRules[(singularityValue, panel)] = sQR if qr.num_nodes > self.temp.shape[0]: self.temp = uninitialized((qr.num_nodes, self.kernel.valueSize), dtype=REAL) - self.qrId = sQR.qr - self.PSI_id = sQR.PSI + self.qrEdge = sQR.qr + self.PSI_edge = sQR.PSI elif panel == COMMON_VERTEX: try: sQR = self.specialQuadRules[(singularityValue, panel)] except KeyError: qr = singularityCancelationQuadRule1D(panel, self.singularityCancelationIntegrandAcrossElements+singularityValue, + log_singularityValue, self.quad_order_diagonal, 2*dm_order) PSI = uninitialized((2*dofs_per_element - dofs_per_vertex, @@ -350,61 +390,10 @@ cdef class fractionalLaplacian1D(nonlocalLaplacian1D): REAL_t[:, ::1] contrib, panelType panel, MASK_t mask=ALL): - cdef: - INDEX_t k, m, i, j, I, J, l - REAL_t vol, vol1 = self.vol1, vol2 = self.vol2, val - REAL_t[:, ::1] simplex1 = self.simplex1 - REAL_t[:, ::1] simplex2 = self.simplex2 - quadratureRule qr - REAL_t[:, ::1] PSI - INDEX_t dofs_per_element = self.DoFMap.dofs_per_element - INDEX_t dim = self.kernel.dim - INDEX_t valueSize = self.kernel.valueSize - REAL_t x[2] - REAL_t y[2] - if panel >= 1: - self.eval_distant(contrib, panel, mask) - return - elif panel == COMMON_EDGE: - qr = self.qrId - PSI = self.PSI_id - elif panel == COMMON_VERTEX: - qr = self.qrVertex - PSI = self.PSI_vertex + self.eval_distant_sym(contrib, panel, mask) else: - raise NotImplementedError('Unknown panel type: {}'.format(panel)) - - vol = vol1*vol2 - - for m in range(qr.num_nodes): - for j in range(dim): - x[j] = (simplex1[self.perm1[0], j]*qr.nodes[0, m] + - simplex1[self.perm1[1], j]*qr.nodes[1, m]) - y[j] = (simplex2[self.perm2[0], j]*qr.nodes[2, m] + - simplex2[self.perm2[1], j]*qr.nodes[3, m]) - self.kernel.evalPtr(dim, - &x[0], - &y[0], - &self.vec[0]) - for l in range(valueSize): - self.temp[m, l] = qr.weights[m] * self.vec[l] - - contrib[:, :] = 0. - for I in range(PSI.shape[0]): - i = self.perm[I] - for J in range(I, PSI.shape[0]): - j = self.perm[J] - if j < i: - k = 2*dofs_per_element*j-(j*(j+1) >> 1) + i - else: - k = 2*dofs_per_element*i-(i*(i+1) >> 1) + j - if mask[k]: - for l in range(valueSize): - val = 0. - for m in range(qr.num_nodes): - val += self.temp[m, l] * PSI[I, m] * PSI[J, m] - contrib[k, l] = val*vol + self.eval_near_sym(contrib, panel, mask) cdef class fractionalLaplacian1D_nonsym(fractionalLaplacian1D): @@ -412,7 +401,7 @@ cdef class fractionalLaplacian1D_nonsym(fractionalLaplacian1D): .. math:: - \\int_{K_1}\\int_{K_2} [ u(x) \\gamma(x,y) - u(y) \\gamma(y,x) ] [ v(x)-v(y) ] dy dx + 0.5 \\int_{K_1}\\int_{K_2} [ u(x) \\gamma(x,y) - u(y) \\gamma(y,x) ] [ v(x)-v(y) ] dy dx for the non-symmetric 1D nonlocal Laplacian. """ @@ -420,11 +409,8 @@ cdef class fractionalLaplacian1D_nonsym(fractionalLaplacian1D): Kernel kernel, meshBase mesh, DoFMap dm, - target_order=None, - quad_order_diagonal=None, - num_dofs=None, **kwargs): - super(fractionalLaplacian1D_nonsym, self).__init__(kernel, mesh, dm, num_dofs, **kwargs) + super(fractionalLaplacian1D_nonsym, self).__init__(kernel, mesh, dm, **kwargs) self.symmetricLocalMatrix = False self.symmetricCells = False @@ -453,6 +439,7 @@ cdef class fractionalLaplacian1D_nonsym(fractionalLaplacian1D): cdef: INDEX_t i REAL_t singularityValue = self.kernel.getSingularityValue() + REAL_t log_singularityValue = self.kernel.getLogSingularityValue() specialQuadRule sQR quadratureRule qr INDEX_t dofs_per_element = self.DoFMap.dofs_per_element @@ -469,9 +456,9 @@ cdef class fractionalLaplacian1D_nonsym(fractionalLaplacian1D): try: sQR = self.specialQuadRules[(singularityValue, panel)] except KeyError: - qr = singularityCancelationQuadRule1D(panel, self.singularityCancelationIntegrandWithinElement+singularityValue, + log_singularityValue, self.quad_order_diagonal, 2*dm_order) PHI = uninitialized((dofs_per_element, @@ -495,15 +482,15 @@ cdef class fractionalLaplacian1D_nonsym(fractionalLaplacian1D): if qr.num_nodes > self.temp.shape[0]: self.temp = uninitialized((qr.num_nodes, self.kernel.valueSize), dtype=REAL) self.temp2 = uninitialized((qr.num_nodes, self.kernel.valueSize), dtype=REAL) - self.qrId = sQR.qr - self.PHI_id = sQR.PHI3 + self.qrEdge = sQR.qr + self.PHI_edge = sQR.PHI3 elif panel == COMMON_VERTEX: try: sQR = self.specialQuadRules[(singularityValue, panel)] except KeyError: - qr = singularityCancelationQuadRule1D(panel, self.singularityCancelationIntegrandAcrossElements+singularityValue, + log_singularityValue, self.quad_order_diagonal, 2*dm_order) PHI = uninitialized((2*dofs_per_element - dofs_per_vertex, @@ -550,57 +537,39 @@ cdef class fractionalLaplacian1D_nonsym(fractionalLaplacian1D): REAL_t[:, ::1] contrib, panelType panel, MASK_t mask=ALL): - cdef: - INDEX_t k, m, i, j, I, J, l - REAL_t vol, vol1 = self.vol1, vol2 = self.vol2, val - REAL_t[:, ::1] simplex1 = self.simplex1 - REAL_t[:, ::1] simplex2 = self.simplex2 - quadratureRule qr - REAL_t[:, :, ::1] PHI - INDEX_t dofs_per_element = self.DoFMap.dofs_per_element - INDEX_t dim = self.DoFMap.mesh.dim - REAL_t x[2] - REAL_t y[2] - INDEX_t valueSize = self.kernel.valueSize - if panel >= 1: self.eval_distant_nonsym(contrib, panel, mask) - return - elif panel == COMMON_EDGE: - qr = self.qrId - PHI = self.PHI_id - elif panel == COMMON_VERTEX: - qr = self.qrVertex - PHI = self.PHI_vertex else: - raise NotImplementedError('Unknown panel type: {}'.format(panel)) + self.eval_near_nonsym(contrib, panel, mask) - vol = vol1*vol2 - - for m in range(qr.num_nodes): - for j in range(dim): - x[j] = (simplex1[self.perm1[0], j]*qr.nodes[0, m] + - simplex1[self.perm1[1], j]*qr.nodes[1, m]) - y[j] = (simplex2[self.perm2[0], j]*qr.nodes[2, m] + - simplex2[self.perm2[1], j]*qr.nodes[3, m]) - self.kernel.evalPtr(dim, &x[0], &y[0], &self.vec[0]) - self.kernel.evalPtr(dim, &y[0], &x[0], &self.vec2[0]) - for l in range(valueSize): - self.temp[m, l] = qr.weights[m] * self.vec[l] - self.temp2[m, l] = qr.weights[m] * self.vec2[l] - - contrib[:, :] = 0. - for I in range(PHI.shape[0]): - i = self.perm[I] - for J in range(PHI.shape[0]): - j = self.perm[J] - k = i*(2*dofs_per_element)+j - if mask[k]: - for l in range(valueSize): - val = 0. - for m in range(qr.num_nodes): - val += (self.temp[m, l] * PHI[I, m, 0] - self.temp2[m, l] * PHI[I, m, 1]) * (PHI[J, m, 0] - PHI[J, m, 1]) - contrib[k, l] = val*vol + +cdef class fractionalLaplacian1D_nonsym2(fractionalLaplacian1D_nonsym): + """The local stiffness matrix + + .. math:: + + 0.5 \\int_{K_1}\\int_{K_2} [ u(x) - u(y) ] [ v(x)-v(y) ] \\gamma(x,y) dy dx + + for the non-symmetric 1D nonlocal Laplacian. + """ + def __init__(self, + Kernel kernel, + meshBase mesh, + DoFMap dm, + target_order=None, + quad_order_diagonal=None, + num_dofs=None, + **kwargs): + super(fractionalLaplacian1D_nonsym2, self).__init__(kernel, mesh, dm, target_order, quad_order_diagonal, num_dofs, **kwargs) + + cdef void eval(self, + REAL_t[:, ::1] contrib, + panelType panel, + MASK_t mask=ALL): + if panel >= 1: + self.eval_distant_nonsym2(contrib, panel, mask) + else: + self.eval_near_nonsym2(contrib, panel, mask) cdef class fractionalLaplacian1D_boundary(fractionalLaplacian1DZeroExterior): @@ -608,7 +577,7 @@ cdef class fractionalLaplacian1D_boundary(fractionalLaplacian1DZeroExterior): .. math:: - \\int_{K}\\int_{e} [ u(x) v(x) n_{y} \\cdot \\frac{x-y}{|x-y|} \\Gamma(x,y) dy dx + \\int_{K}\\int_{e} u(x) v(x) n_{y} \\cdot \\Gamma(x,y) dy dx for the 1D nonlocal Laplacian. """ @@ -638,7 +607,7 @@ cdef class fractionalLaplacian1D_boundary(fractionalLaplacian1DZeroExterior): quad_order_diagonal = max(np.ceil(((target_order+1.)*log(self.num_dofs*self.H0)+(2.*smax-1.)*abs(log(self.hmin/self.H0)))/0.8), 2) self.quad_order_diagonal = quad_order_diagonal - if (self.kernel.kernelType != FRACTIONAL) or (not self.kernel.variableOrder): + if (self.kernel.kernelType != FRACTIONAL) or (not self.kernel.variableSingularity): self.getNearQuadRule(COMMON_VERTEX) cdef panelType getQuadOrder(self, @@ -672,6 +641,7 @@ cdef class fractionalLaplacian1D_boundary(fractionalLaplacian1DZeroExterior): cdef: INDEX_t i REAL_t singularityValue = self.kernel.getSingularityValue() + REAL_t log_singularityValue = self.kernel.getLogSingularityValue() specialQuadRule sQR quadratureRule qr INDEX_t dof @@ -684,11 +654,10 @@ cdef class fractionalLaplacian1D_boundary(fractionalLaplacian1DZeroExterior): try: sQR = self.specialQuadRules[(singularityValue, panel)] except KeyError: - if singularityValue > -1.+1e-3: - qr = singularityCancelationQuadRule1D_boundary(panel, singularityValue, self.quad_order_diagonal, 1) + qr = singularityCancelationQuadRule1D_boundary(panel, singularityValue, log_singularityValue, self.quad_order_diagonal, 1) else: - qr = singularityCancelationQuadRule1D_boundary(panel, 2.+singularityValue, self.quad_order_diagonal, 1) + qr = singularityCancelationQuadRule1D_boundary(panel, 2.+singularityValue, log_singularityValue, self.quad_order_diagonal, 1) PHI = uninitialized((dofs_per_element, qr.num_nodes), dtype=REAL) for dof in range(dofs_per_element): @@ -704,7 +673,7 @@ cdef class fractionalLaplacian1D_boundary(fractionalLaplacian1DZeroExterior): if qr.num_nodes > self.temp.shape[0]: self.temp = uninitialized((qr.num_nodes, self.kernel.valueSize), dtype=REAL) self.qrVertex = sQR.qr - self.PHI_vertex = sQR.PHI + self.PHI_vertex2 = sQR.PHI else: raise NotImplementedError('Unknown panel type: {}'.format(panel)) @@ -720,66 +689,7 @@ cdef class fractionalLaplacian1D_boundary(fractionalLaplacian1DZeroExterior): REAL_t[:, ::1] contrib, panelType panel, MASK_t mask=ALL): - cdef: - REAL_t vol = self.vol1, val - INDEX_t i, j, k, m, l - quadratureRule qr - REAL_t[:, ::1] PHI - REAL_t[:, ::1] simplex1 = self.simplex1 - REAL_t[:, ::1] simplex2 = self.simplex2 - INDEX_t dim = self.DoFMap.mesh.dim - REAL_t x[2] - REAL_t y[2] - INDEX_t dofs_per_element = self.DoFMap.dofs_per_element - INDEX_t valueSize = self.kernel.valueSize - - # Kernel: - # \Gamma(x,y) = n \dot (x-y) * C(d,s) / (2s) / |x-y|^{d+2s} - # with inward normal n. - # - # Rewrite as - # \Gamma(x,y) = [ n \dot (x-y)/|x-y| ] * [ C(d,s) / (2s) / |x-y|^{d-1+2s} ] - # \--------------------------------/ - # | - # boundaryKernel - # - # In 1D: - # n = (x-y)/|x-y| - # so - # n \dot (x-y) / |x-y| = 1 - if panel >= 1: self.eval_distant_boundary(contrib, panel, mask) - elif panel == COMMON_VERTEX: - qr = self.qrVertex - PHI = self.PHI_vertex - - for m in range(qr.num_nodes): - for j in range(dim): - x[j] = (simplex1[self.perm1[0], j]*qr.nodes[0, m] + - simplex1[self.perm1[1], j]*qr.nodes[1, m]) - y[j] = simplex2[self.perm2[0], j]*qr.nodes[2, m] - self.kernel.evalPtr(dim, &x[0], &y[0], &self.vec[0]) - for l in range(valueSize): - self.temp[m, l] = qr.weights[m] * self.vec[l] - - contrib[:, :] = 0. - - for I in range(dofs_per_element): - i = self.perm[I] - for J in range(I, dofs_per_element): - j = self.perm[J] - if j < i: - k = dofs_per_element*j-(j*(j+1) >> 1) + i - else: - k = dofs_per_element*i-(i*(i+1) >> 1) + j - if mask[k]: - for l in range(valueSize): - val = 0. - for m in range(qr.num_nodes): - val += self.temp[m, l] * PHI[I, m] * PHI[J, m] - contrib[k, l] = val*vol else: - raise NotImplementedError('Unknown panel type: {}'.format(panel)) - - + self.eval_near_boundary(contrib, panel, mask) diff --git a/nl/PyNucleus_nl/fractionalLaplacian2D.pxd b/nl/PyNucleus_nl/fractionalLaplacian2D.pxd index 455bb7f2..f779fa7d 100644 --- a/nl/PyNucleus_nl/fractionalLaplacian2D.pxd +++ b/nl/PyNucleus_nl/fractionalLaplacian2D.pxd @@ -14,39 +14,33 @@ from PyNucleus_fem.meshCy cimport meshBase from . nonlocalOperator cimport (double_local_matrix_t, nonlocalLaplacian2D, specialQuadRule, + singularityCancelationQuadRule, panelType, MASK_t) from . fractionalOrders cimport fractionalOrderBase -from . kernelsCy cimport (Kernel, - FractionalKernel) +from . kernels cimport (Kernel, + FractionalKernel) cdef class fractionalLaplacian2DZeroExterior(nonlocalLaplacian2D): - cdef: - public REAL_t[:, :, ::1] PHI_edge, PSI_edge, PHI_vertex, PSI_vertex - public REAL_t[:, ::1] PHI_edge2, PHI_vertex2 - + pass -cdef class singularityCancelationQuadRule2D(quadratureRule): +cdef class singularityCancelationQuadRule2D(singularityCancelationQuadRule): pass cdef class fractionalLaplacian2D(nonlocalLaplacian2D): cdef: - public quadratureRule qrEdge, qrVertex, qrId - public REAL_t[:, ::1] PSI_edge, PSI_id, PSI_vertex REAL_t singularityCancelationIntegrandWithinElement REAL_t singularityCancelationIntegrandAcrossElements cdef class fractionalLaplacian2D_nonsym(fractionalLaplacian2D): - cdef: - public REAL_t[:, :, ::1] PHI_edge, PHI_id, PHI_vertex - + pass -cdef class fractionalLaplacian2D_boundary(fractionalLaplacian2DZeroExterior): - cdef: - public quadQuadratureRule qrVertex0, qrVertex1 - public quadratureRule qrEdge, qrVertex +cdef class fractionalLaplacian2D_nonsym2(fractionalLaplacian2D_nonsym): + pass +cdef class fractionalLaplacian2D_boundary(fractionalLaplacian2DZeroExterior): + pass diff --git a/nl/PyNucleus_nl/fractionalLaplacian2D.pyx b/nl/PyNucleus_nl/fractionalLaplacian2D.pyx index 8003f881..82df1f68 100644 --- a/nl/PyNucleus_nl/fractionalLaplacian2D.pyx +++ b/nl/PyNucleus_nl/fractionalLaplacian2D.pyx @@ -13,7 +13,8 @@ from PyNucleus_base.myTypes import INDEX, REAL from PyNucleus_base import uninitialized from PyNucleus_base.blas cimport mydot from PyNucleus_fem.quadrature cimport (transformQuadratureRule, - doubleSimplexQuadratureRule, GaussJacobi) + doubleSimplexQuadratureRule, + GaussJacobi, LogGaussJacobi) from PyNucleus_fem.DoFMaps cimport DoFMap, P0_DoFMap, P1_DoFMap, shapeFunction from PyNucleus_nl.fractionalOrders cimport constFractionalOrder @@ -27,15 +28,16 @@ ALL.set() cdef class fractionalLaplacian2DZeroExterior(nonlocalLaplacian2D): def __init__(self, Kernel kernel, meshBase mesh, DoFMap dm, num_dofs=None, **kwargs): - manifold_dim2 = mesh.dim-1 + manifold_dim2 = mesh.manifold_dim-1 super(fractionalLaplacian2DZeroExterior, self).__init__(kernel, mesh, dm, num_dofs, manifold_dim2=manifold_dim2, **kwargs) self.symmetricCells = False self.symmetricLocalMatrix = True -cdef class singularityCancelationQuadRule2D(quadratureRule): +cdef class singularityCancelationQuadRule2D(singularityCancelationQuadRule): def __init__(self, panelType panel, REAL_t singularity, + REAL_t log_singularity, INDEX_t quad_order_diagonal, INDEX_t quad_order_diagonalV, INDEX_t quad_order_regular): @@ -48,6 +50,7 @@ cdef class singularityCancelationQuadRule2D(quadratureRule): REAL_t lcl_bary_y[3] REAL_t[:, ::1] bary, bary_x, bary_y REAL_t[::1] weights + REAL_t[::1] singularPart INDEX_t offset if panel == COMMON_FACE: @@ -61,19 +64,66 @@ cdef class singularityCancelationQuadRule2D(quadratureRule): # We factor out (eta0 * eta1 * eta2) from each PSI and # (eta0 * eta1 * eta2)**singularity from the kernel. - # Differences of basis functions one the same element - # cancel one singularity order i.e. - # singularityCancelationFromContinuity = 1. - qrId = GaussJacobi(((1, 3+singularity, 0), - (1, 2+singularity, 0), - (1, 1+singularity, 0), - (quad_order_diagonal, 0, 0))) + if log_singularity == 0: + qrId = GaussJacobi(((1, 3+singularity, 0), + (1, 2+singularity, 0), + (1, 1+singularity, 0), + (quad_order_diagonal, 0, 0))) + elif log_singularity == 1: + qr1000 = LogGaussJacobi(((1, 3+singularity, 0, 1), + (1, 2+singularity, 0, 0), + (1, 1+singularity, 0, 0), + (quad_order_diagonal, 0, 0, 0))) + qr0100 = LogGaussJacobi(((1, 3+singularity, 0, 0), + (1, 2+singularity, 0, 1), + (1, 1+singularity, 0, 0), + (quad_order_diagonal, 0, 0, 0))) + qr0010 = LogGaussJacobi(((1, 3+singularity, 0, 0), + (1, 2+singularity, 0, 0), + (1, 1+singularity, 0, 1), + (quad_order_diagonal, 0, 0, 0))) + qrId = qr1000+qr0100+qr0010 + elif log_singularity == 2: + qr2000 = LogGaussJacobi(((1, 3+singularity, 0, 2), + (1, 2+singularity, 0, 0), + (1, 1+singularity, 0, 0), + (quad_order_diagonal, 0, 0, 0))) + qr1100 = LogGaussJacobi(((1, 3+singularity, 0, 1), + (1, 2+singularity, 0, 1), + (1, 1+singularity, 0, 0), + (quad_order_diagonal, 0, 0, 0))) + qr0200 = LogGaussJacobi(((1, 3+singularity, 0, 0), + (1, 2+singularity, 0, 2), + (1, 1+singularity, 0, 0), + (quad_order_diagonal, 0, 0, 0))) + qr0110 = LogGaussJacobi(((1, 3+singularity, 0, 0), + (1, 2+singularity, 0, 1), + (1, 1+singularity, 0, 1), + (quad_order_diagonal, 0, 0, 0))) + qr0020 = LogGaussJacobi(((1, 3+singularity, 0, 0), + (1, 2+singularity, 0, 0), + (1, 1+singularity, 0, 2), + (quad_order_diagonal, 0, 0, 0))) + qr1010 = LogGaussJacobi(((1, 3+singularity, 0, 1), + (1, 2+singularity, 0, 0), + (1, 1+singularity, 0, 1), + (quad_order_diagonal, 0, 0, 0))) + for i in range(qr1100.num_nodes): + qr1100.weights[i] *= 2. + for i in range(qr0110.num_nodes): + qr0110.weights[i] *= 2. + for i in range(qr1010.num_nodes): + qr1010.weights[i] *= 2. + qrId = qr2000+qr0200+qr0020+qr1100+qr0110+qr1010 + else: + raise NotImplementedError(log_singularity) bary = uninitialized((2*dim+2, 3*qrId.num_nodes), dtype=REAL) bary_x = bary[:dim+1, :] bary_y = bary[dim+1:, :] weights = uninitialized((3*qrId.num_nodes), dtype=REAL) + singularPart = uninitialized((3*qrId.num_nodes), dtype=REAL) # integral 0 offset = 0 @@ -104,7 +154,8 @@ cdef class singularityCancelationQuadRule2D(quadratureRule): bary_y[1, offset+i] = lcl_bary_y[1] bary_y[2, offset+i] = lcl_bary_y[2] - weights[offset+i] = 2.0*qrId.weights[i]*(eta0*eta1*eta2)**(-singularity) + singularPart[offset+i] = eta0*eta1*eta2 + weights[offset+i] = 2.0*qrId.weights[i]*singularPart[offset+i]**(-singularity) # integral 1 offset = qrId.num_nodes @@ -135,7 +186,8 @@ cdef class singularityCancelationQuadRule2D(quadratureRule): bary_y[1, offset+i] = lcl_bary_y[1] bary_y[2, offset+i] = lcl_bary_y[2] - weights[offset+i] = 2.0*qrId.weights[i]*(eta0*eta1*eta2)**(-singularity) + singularPart[offset+i] = eta0*eta1*eta2 + weights[offset+i] = 2.0*qrId.weights[i]*singularPart[offset+i]**(-singularity) # integral 2 offset = 2*qrId.num_nodes @@ -166,7 +218,8 @@ cdef class singularityCancelationQuadRule2D(quadratureRule): bary_y[1, offset+i] = lcl_bary_y[1] bary_y[2, offset+i] = lcl_bary_y[2] - weights[offset+i] = 2.0*qrId.weights[i]*(eta0*eta1*eta2)**(-singularity) + singularPart[offset+i] = eta0*eta1*eta2 + weights[offset+i] = 2.0*qrId.weights[i]*singularPart[offset+i]**(-singularity) super(singularityCancelationQuadRule2D, self).__init__(bary, weights, dim+1) elif panel == COMMON_EDGE: @@ -178,20 +231,74 @@ cdef class singularityCancelationQuadRule2D(quadratureRule): # We factor out (eta0 * eta1) from each PSI and # (eta0 * eta1)**singularity from the kernel. - qrEdge0 = GaussJacobi(((1, 3+singularity, 0), - (1, 2+singularity, 0), - (quad_order_diagonal, 0, 0), - (quad_order_diagonal, 0, 0))) - qrEdge1 = GaussJacobi(((1, 3+singularity, 0), - (1, 2+singularity, 0), - (quad_order_diagonal, 1, 0), - (quad_order_diagonal, 0, 0))) + if log_singularity == 0: + qrEdge0 = GaussJacobi(((1, 3+singularity, 0), + (1, 2+singularity, 0), + (quad_order_diagonal, 0, 0), + (quad_order_diagonal, 0, 0))) + qrEdge1 = GaussJacobi(((1, 3+singularity, 0), + (1, 2+singularity, 0), + (quad_order_diagonal, 1, 0), + (quad_order_diagonal, 0, 0))) + elif log_singularity == 1: + qrEdge0_1000 = LogGaussJacobi(((1, 3+singularity, 0, 1), + (1, 2+singularity, 0, 0), + (quad_order_diagonal, 0, 0, 0), + (quad_order_diagonal, 0, 0, 0))) + qrEdge0_0100 = LogGaussJacobi(((1, 3+singularity, 0, 0), + (1, 2+singularity, 0, 1), + (quad_order_diagonal, 0, 0, 0), + (quad_order_diagonal, 0, 0, 0))) + qrEdge0 = qrEdge0_1000+qrEdge0_0100 + qrEdge1_1000 = LogGaussJacobi(((1, 3+singularity, 0, 1), + (1, 2+singularity, 0, 0), + (quad_order_diagonal, 1, 0, 0), + (quad_order_diagonal, 0, 0, 0))) + qrEdge1_0100 = LogGaussJacobi(((1, 3+singularity, 0, 0), + (1, 2+singularity, 0, 1), + (quad_order_diagonal, 1, 0, 0), + (quad_order_diagonal, 0, 0, 0))) + qrEdge1 = qrEdge1_1000+qrEdge1_0100 + elif log_singularity == 2: + qrEdge0_2000 = LogGaussJacobi(((1, 3+singularity, 0, 1), + (1, 2+singularity, 0, 0), + (quad_order_diagonal, 0, 0, 0), + (quad_order_diagonal, 0, 0, 0))) + qrEdge0_1100 = LogGaussJacobi(((1, 3+singularity, 0, 1), + (1, 2+singularity, 0, 1), + (quad_order_diagonal, 0, 0, 0), + (quad_order_diagonal, 0, 0, 0))) + qrEdge0_0200 = LogGaussJacobi(((1, 3+singularity, 0, 0), + (1, 2+singularity, 0, 2), + (quad_order_diagonal, 0, 0, 0), + (quad_order_diagonal, 0, 0, 0))) + for i in range(qrEdge0_1100.num_nodes): + qrEdge0_1100.weights[i] *= 2. + qrEdge0 = qrEdge0_2000+qrEdge0_1100+qrEdge0_0200 + qrEdge1_2000 = LogGaussJacobi(((1, 3+singularity, 0, 2), + (1, 2+singularity, 0, 0), + (quad_order_diagonal, 1, 0, 0), + (quad_order_diagonal, 0, 0, 0))) + qrEdge1_1100 = LogGaussJacobi(((1, 3+singularity, 0, 1), + (1, 2+singularity, 0, 1), + (quad_order_diagonal, 1, 0, 0), + (quad_order_diagonal, 0, 0, 0))) + qrEdge1_0200 = LogGaussJacobi(((1, 3+singularity, 0, 0), + (1, 2+singularity, 0, 2), + (quad_order_diagonal, 1, 0, 0), + (quad_order_diagonal, 0, 0, 0))) + for i in range(qrEdge1_1100.num_nodes): + qrEdge1_1100.weights[i] *= 2. + qrEdge1 = qrEdge1_2000+qrEdge1_1100+qrEdge1_0200 + else: + raise NotImplementedError(log_singularity) bary = uninitialized((2*dim+2, 2*(qrEdge0.num_nodes+qrEdge1.num_nodes)), dtype=REAL) bary_x = bary[:dim+1, :] bary_y = bary[dim+1:, :] weights = uninitialized((2*(qrEdge0.num_nodes+qrEdge1.num_nodes)), dtype=REAL) + singularPart = uninitialized((2*(qrEdge0.num_nodes+qrEdge1.num_nodes)), dtype=REAL) # integral 0 offset = 0 @@ -222,7 +329,8 @@ cdef class singularityCancelationQuadRule2D(quadratureRule): bary_y[1, offset+i] = lcl_bary_y[1] bary_y[2, offset+i] = lcl_bary_y[2] - weights[offset+i] = qrEdge0.weights[i] * (eta0*eta1)**(-singularity) + singularPart[offset+i] = eta0*eta1 + weights[offset+i] = qrEdge0.weights[i] * singularPart[offset+i]**(-singularity) # integral 1 offset = qrEdge0.num_nodes @@ -253,7 +361,8 @@ cdef class singularityCancelationQuadRule2D(quadratureRule): bary_y[1, offset+i] = lcl_bary_y[1] bary_y[2, offset+i] = lcl_bary_y[2] - weights[offset+i] = qrEdge0.weights[i] * (eta0*eta1)**(-singularity) + singularPart[offset+i] = eta0*eta1 + weights[offset+i] = qrEdge0.weights[i] * singularPart[offset+i]**(-singularity) # integral 2 offset = 2*qrEdge0.num_nodes @@ -284,7 +393,8 @@ cdef class singularityCancelationQuadRule2D(quadratureRule): bary_y[1, offset+i] = lcl_bary_y[1] bary_y[2, offset+i] = lcl_bary_y[2] - weights[offset+i] = qrEdge1.weights[i] * (eta0*eta1)**(-singularity) + singularPart[offset+i] = eta0*eta1 + weights[offset+i] = qrEdge1.weights[i] * singularPart[offset+i]**(-singularity) # integral 3 offset = 2*qrEdge0.num_nodes+qrEdge1.num_nodes @@ -315,7 +425,8 @@ cdef class singularityCancelationQuadRule2D(quadratureRule): bary_y[1, offset+i] = lcl_bary_y[1] bary_y[2, offset+i] = lcl_bary_y[2] - weights[offset+i] = qrEdge1.weights[i] * (eta0*eta1)**(-singularity) + singularPart[offset+i] = eta0*eta1 + weights[offset+i] = qrEdge1.weights[i] * singularPart[offset+i]**(-singularity) super(singularityCancelationQuadRule2D, self).__init__(bary, weights, 2*dim) elif panel == COMMON_VERTEX: @@ -326,15 +437,22 @@ cdef class singularityCancelationQuadRule2D(quadratureRule): # We factor out eta0 from each PSI and # eta0**singularity from the kernel. - qrVertex = GaussJacobi(((1, 3+singularity, 0), - (quad_order_diagonalV, 0, 0), - (quad_order_diagonalV, 1, 0), - (quad_order_diagonalV, 0, 0))) + if log_singularity == 0: + qrVertex = GaussJacobi(((1, 3+singularity, 0), + (quad_order_diagonalV, 0, 0), + (quad_order_diagonalV, 1, 0), + (quad_order_diagonalV, 0, 0))) + else: + qrVertex = LogGaussJacobi(((1, 3+singularity, 0, log_singularity), + (quad_order_diagonalV, 0, 0, 0), + (quad_order_diagonalV, 1, 0, 0), + (quad_order_diagonalV, 0, 0, 0))) bary = uninitialized((2*dim+2, 2*qrVertex.num_nodes), dtype=REAL) bary_x = bary[:dim+1, :] bary_y = bary[dim+1:, :] weights = uninitialized((2*qrVertex.num_nodes), dtype=REAL) + singularPart = uninitialized((2*qrVertex.num_nodes), dtype=REAL) offset = 0 for i in range(qrVertex.num_nodes): @@ -364,7 +482,8 @@ cdef class singularityCancelationQuadRule2D(quadratureRule): bary_y[1, offset+i] = lcl_bary_y[1] bary_y[2, offset+i] = lcl_bary_y[2] - weights[offset+i] = qrVertex.weights[i] * eta0**(-singularity) + singularPart[offset+i] = eta0 + weights[offset+i] = qrVertex.weights[i] * singularPart[offset+i]**(-singularity) offset = qrVertex.num_nodes for i in range(qrVertex.num_nodes): @@ -394,14 +513,17 @@ cdef class singularityCancelationQuadRule2D(quadratureRule): bary_y[1, offset+i] = lcl_bary_y[1] bary_y[2, offset+i] = lcl_bary_y[2] - weights[offset+i] = qrVertex.weights[i] * eta0**(-singularity) + singularPart[offset+i] = eta0 + weights[offset+i] = qrVertex.weights[i] * singularPart[offset+i]**(-singularity) super(singularityCancelationQuadRule2D, self).__init__(bary, weights, 2*dim+1) + self.singularPart = singularPart -cdef class singularityCancelationQuadRule2D_boundary(quadratureRule): +cdef class singularityCancelationQuadRule2D_boundary(singularityCancelationQuadRule): def __init__(self, panelType panel, REAL_t singularity, + REAL_t log_singularity, INDEX_t quad_order_diagonal, INDEX_t quad_order_regular): cdef: @@ -413,11 +535,17 @@ cdef class singularityCancelationQuadRule2D_boundary(quadratureRule): REAL_t lcl_bary_y[2] REAL_t[:, ::1] bary, bary_x, bary_y REAL_t[::1] weights + REAL_t[::1] singularPart if panel == COMMON_EDGE: - qrEdge0 = qrEdge1 = qrEdge2 = GaussJacobi(((quad_order_regular, 1.+singularity, 1.), - (quad_order_diagonal, 0., 0.), - (quad_order_diagonal, 0., 0.))) + if log_singularity == 0: + qrEdge0 = qrEdge1 = qrEdge2 = GaussJacobi(((quad_order_regular, 1.+singularity, 1.), + (quad_order_diagonal, 0., 0.), + (quad_order_diagonal, 0., 0.))) + else: + qrEdge0 = qrEdge1 = qrEdge2 = LogGaussJacobi(((quad_order_regular, 1.+singularity, 1., log_singularity), + (quad_order_diagonal, 0., 0., 0.), + (quad_order_diagonal, 0., 0., 0.))) bary = uninitialized((2*dim+1, qrEdge0.num_nodes+ @@ -428,6 +556,9 @@ cdef class singularityCancelationQuadRule2D_boundary(quadratureRule): weights = uninitialized((qrEdge0.num_nodes+ qrEdge1.num_nodes+ qrEdge2.num_nodes), dtype=REAL) + singularPart = uninitialized((qrEdge0.num_nodes+ + qrEdge1.num_nodes+ + qrEdge2.num_nodes), dtype=REAL) # int 0 offset = 0 @@ -450,7 +581,8 @@ cdef class singularityCancelationQuadRule2D_boundary(quadratureRule): bary_y[0, offset+i] = lcl_bary_y[0] bary_y[1, offset+i] = lcl_bary_y[1] - weights[offset+i] = qrEdge0.weights[i] * eta0**(-singularity) + singularPart[offset+i] = eta0 + weights[offset+i] = qrEdge0.weights[i] * singularPart[offset+i]**(-singularity) # int 1 offset = qrEdge0.num_nodes @@ -473,7 +605,8 @@ cdef class singularityCancelationQuadRule2D_boundary(quadratureRule): bary_y[0, offset+i] = lcl_bary_y[0] bary_y[1, offset+i] = lcl_bary_y[1] - weights[offset+i] = qrEdge1.weights[i] * eta0**(-singularity) + singularPart[offset+i] = eta0 + weights[offset+i] = qrEdge1.weights[i] * singularPart[offset+i]**(-singularity) # int 2 offset = qrEdge0.num_nodes+qrEdge1.num_nodes @@ -496,16 +629,25 @@ cdef class singularityCancelationQuadRule2D_boundary(quadratureRule): bary_y[0, offset+i] = lcl_bary_y[0] bary_y[1, offset+i] = lcl_bary_y[1] - weights[offset+i] = qrEdge2.weights[i] * eta0**(-singularity) + singularPart[offset+i] = eta0 + weights[offset+i] = qrEdge2.weights[i] * singularPart[offset+i]**(-singularity) super(singularityCancelationQuadRule2D_boundary, self).__init__(bary, weights, 2*dim+1) elif panel == COMMON_VERTEX: - qrVertex0 = GaussJacobi(((quad_order_regular, 2.0+singularity, 0), - (quad_order_diagonal, 0, 0), - (quad_order_diagonal, 0, 0))) - qrVertex1 = GaussJacobi(((quad_order_regular, 2.0+singularity, 0), - (quad_order_diagonal, 1, 0), - (quad_order_diagonal, 0, 0))) + if log_singularity == 0: + qrVertex0 = GaussJacobi(((quad_order_regular, 2.0+singularity, 0), + (quad_order_diagonal, 0, 0), + (quad_order_diagonal, 0, 0))) + qrVertex1 = GaussJacobi(((quad_order_regular, 2.0+singularity, 0), + (quad_order_diagonal, 1, 0), + (quad_order_diagonal, 0, 0))) + else: + qrVertex0 = LogGaussJacobi(((quad_order_regular, 2.0+singularity, 0, 1), + (quad_order_diagonal, 0, 0, 0), + (quad_order_diagonal, 0, 0, 0))) + qrVertex1 = LogGaussJacobi(((quad_order_regular, 2.0+singularity, 0, 1), + (quad_order_diagonal, 1, 0, 0), + (quad_order_diagonal, 0, 0, 0))) bary = uninitialized((2*dim+1, qrVertex0.num_nodes+ qrVertex1.num_nodes), dtype=REAL) @@ -513,6 +655,8 @@ cdef class singularityCancelationQuadRule2D_boundary(quadratureRule): bary_y = bary[dim+1:, :] weights = uninitialized((qrVertex0.num_nodes+ qrVertex1.num_nodes), dtype=REAL) + singularPart = uninitialized((qrVertex0.num_nodes+ + qrVertex1.num_nodes), dtype=REAL) # int 0 offset = 0 @@ -535,7 +679,8 @@ cdef class singularityCancelationQuadRule2D_boundary(quadratureRule): bary_y[0, offset+i] = lcl_bary_y[0] bary_y[1, offset+i] = lcl_bary_y[1] - weights[offset+i] = qrVertex0.weights[i] * eta0**(-singularity) + singularPart[offset+i] = eta0 + weights[offset+i] = qrVertex0.weights[i] * singularPart[offset+i]**(-singularity) # int 1 offset = qrVertex0.num_nodes @@ -558,9 +703,11 @@ cdef class singularityCancelationQuadRule2D_boundary(quadratureRule): bary_y[0, offset+i] = lcl_bary_y[0] bary_y[1, offset+i] = lcl_bary_y[1] - weights[offset+i] = qrVertex1.weights[i] * eta0**(-singularity) + singularPart[offset+i] = eta0 + weights[offset+i] = qrVertex1.weights[i] * singularPart[offset+i]**(-singularity) super(singularityCancelationQuadRule2D_boundary, self).__init__(bary, weights, 2*dim+1) + self.singularPart = singularPart cdef class fractionalLaplacian2D(nonlocalLaplacian2D): @@ -568,7 +715,7 @@ cdef class fractionalLaplacian2D(nonlocalLaplacian2D): .. math:: - \\int_{K_1}\\int_{K_2} (u(x)-u(y)) (v(x)-v(y)) \\gamma(x,y) dy dx + 0.5 \\int_{K_1}\\int_{K_2} (u(x)-u(y)) (v(x)-v(y)) \\gamma(x,y) dy dx for the symmetric 2D nonlocal Laplacian. """ @@ -614,7 +761,7 @@ cdef class fractionalLaplacian2D(nonlocalLaplacian2D): self.quad_order_diagonal = quad_order_diagonal self.quad_order_diagonalV = quad_order_diagonalV - if (self.kernel.kernelType != FRACTIONAL) or (not self.kernel.variableOrder): + if (self.kernel.kernelType != FRACTIONAL) or (not self.kernel.variableSingularity): self.getNearQuadRule(COMMON_FACE) self.getNearQuadRule(COMMON_EDGE) self.getNearQuadRule(COMMON_VERTEX) @@ -645,8 +792,9 @@ cdef class fractionalLaplacian2D(nonlocalLaplacian2D): cdef: INDEX_t i REAL_t singularityValue = self.kernel.getSingularityValue() + REAL_t log_singularityValue = self.kernel.getLogSingularityValue() specialQuadRule sQR - quadratureRule qr + singularityCancelationQuadRule qr INDEX_t dofs_per_element = self.DoFMap.dofs_per_element INDEX_t dofs_per_edge = self.DoFMap.dofs_per_edge INDEX_t dofs_per_vertex = self.DoFMap.dofs_per_vertex @@ -666,9 +814,11 @@ cdef class fractionalLaplacian2D(nonlocalLaplacian2D): qr = singularityCancelationQuadRule2D(panel, self.singularityCancelationIntegrandWithinElement+singularityValue, + log_singularityValue, self.quad_order_diagonal, self.quad_order_diagonalV, 1) + qr.scaleWeights(4.0) PSI = uninitialized((dofs_per_element, qr.num_nodes), dtype=REAL) for dof in range(dofs_per_element): @@ -688,8 +838,8 @@ cdef class fractionalLaplacian2D(nonlocalLaplacian2D): self.specialQuadRules[(singularityValue, panel)] = sQR if qr.num_nodes > self.temp.shape[0]: self.temp = uninitialized((qr.num_nodes, self.kernel.valueSize), dtype=REAL) - self.qrId = sQR.qr - self.PSI_id = sQR.PSI + self.qrFace = sQR.qr + self.PSI_face = sQR.PSI elif panel == COMMON_EDGE: try: sQR = self.specialQuadRules[(singularityValue, panel)] @@ -697,9 +847,11 @@ cdef class fractionalLaplacian2D(nonlocalLaplacian2D): qr = singularityCancelationQuadRule2D(panel, self.singularityCancelationIntegrandAcrossElements+singularityValue, + log_singularityValue, self.quad_order_diagonal, self.quad_order_diagonalV, 1) + qr.scaleWeights(4.0) PSI = uninitialized((2*dofs_per_element - 2*dofs_per_vertex - dofs_per_edge, qr.num_nodes), dtype=REAL) @@ -770,9 +922,11 @@ cdef class fractionalLaplacian2D(nonlocalLaplacian2D): qr = singularityCancelationQuadRule2D(panel, self.singularityCancelationIntegrandAcrossElements+singularityValue, + log_singularityValue, self.quad_order_diagonal, self.quad_order_diagonalV, 1) + qr.scaleWeights(4.0) PSI = uninitialized((2*dofs_per_element - dofs_per_vertex, qr.num_nodes), dtype=REAL) @@ -824,71 +978,10 @@ cdef class fractionalLaplacian2D(nonlocalLaplacian2D): REAL_t[:, ::1] contrib, panelType panel, MASK_t mask=ALL): - cdef: - INDEX_t k, m, i, j, I, J, dofs_per_element, dim = self.DoFMap.mesh.dim, l - INDEX_t valueSize = self.kernel.valueSize - REAL_t vol, val - quadratureRule qr - REAL_t[:, ::1] PSI - REAL_t x[3] - REAL_t y[3] - if panel >= 1: - self.eval_distant(contrib, panel, mask) - return - elif panel == COMMON_FACE: - qr = self.qrId - PSI = self.PSI_id - elif panel == COMMON_EDGE: - qr = self.qrEdge - PSI = self.PSI_edge - elif panel == COMMON_VERTEX: - qr = self.qrVertex - PSI = self.PSI_vertex + self.eval_distant_sym(contrib, panel, mask) else: - raise NotImplementedError('Unknown panel type: {}'.format(panel)) - - vol = 4.0*self.vol1*self.vol2 - - dofs_per_element = self.DoFMap.dofs_per_element - - # Evaluate the kernel on the quadrature nodes - for m in range(qr.num_nodes): - for j in range(dim): - x[j] = (self.simplex1[self.perm1[0], j]*qr.nodes[0, m] + - self.simplex1[self.perm1[1], j]*qr.nodes[1, m] + - self.simplex1[self.perm1[2], j]*qr.nodes[2, m]) - y[j] = (self.simplex2[self.perm2[0], j]*qr.nodes[3, m] + - self.simplex2[self.perm2[1], j]*qr.nodes[4, m] + - self.simplex2[self.perm2[2], j]*qr.nodes[5, m]) - self.kernel.evalPtr(dim, - &x[0], - &y[0], - &self.vec[0]) - for l in range(valueSize): - self.temp[m, l] = qr.weights[m] * self.vec[l] - - # "perm" maps from dofs on the reordered simplices (matching - # vertices first) to the dofs in the usual ordering. - contrib[:] = 0. - for I in range(PSI.shape[0]): - i = self.perm[I] - for J in range(I, PSI.shape[0]): - j = self.perm[J] - # We are assembling the upper trinagular part of the - # symmetric (2*dofs_per_element)**2 local stiffness - # matrix. This computes the flattened index. - if j < i: - k = 2*dofs_per_element*j-(j*(j+1) >> 1) + i - else: - k = 2*dofs_per_element*i-(i*(i+1) >> 1) + j - # Check if that entry has been requested. - if mask[k]: - for l in range(valueSize): - val = 0. - for m in range(qr.num_nodes): - val += self.temp[m, l] * PSI[I, m] * PSI[J, m] - contrib[k, l] = val*vol + self.eval_near_sym(contrib, panel, mask) cdef class fractionalLaplacian2D_nonsym(fractionalLaplacian2D): @@ -896,7 +989,7 @@ cdef class fractionalLaplacian2D_nonsym(fractionalLaplacian2D): .. math:: - \\int_{K_1}\\int_{K_2} [ u(x) \\gamma(x,y) - u(y) \\gamma(y,x) ] [ v(x)-v(y) ] dy dx + 0.5 \\int_{K_1}\\int_{K_2} [ u(x) \\gamma(x,y) - u(y) \\gamma(y,x) ] [ v(x)-v(y) ] dy dx for the 2D non-symmetric nonlocal Laplacian. """ @@ -938,8 +1031,9 @@ cdef class fractionalLaplacian2D_nonsym(fractionalLaplacian2D): cdef: INDEX_t i REAL_t singularityValue = self.kernel.getSingularityValue() + REAL_t log_singularityValue = self.kernel.getLogSingularityValue() specialQuadRule sQR - quadratureRule qr + singularityCancelationQuadRule qr INDEX_t dofs_per_element = self.DoFMap.dofs_per_element INDEX_t dofs_per_edge = self.DoFMap.dofs_per_edge INDEX_t dofs_per_vertex = self.DoFMap.dofs_per_vertex @@ -959,9 +1053,11 @@ cdef class fractionalLaplacian2D_nonsym(fractionalLaplacian2D): qr = singularityCancelationQuadRule2D(panel, self.singularityCancelationIntegrandWithinElement+singularityValue, + log_singularityValue, self.quad_order_diagonal, self.quad_order_diagonalV, 1) + qr.scaleWeights(4.0) PHI = uninitialized((dofs_per_element, qr.num_nodes, 2), dtype=REAL) for dof in range(dofs_per_element): @@ -983,8 +1079,8 @@ cdef class fractionalLaplacian2D_nonsym(fractionalLaplacian2D): if qr.num_nodes > self.temp.shape[0]: self.temp = uninitialized((qr.num_nodes, self.kernel.valueSize), dtype=REAL) self.temp2 = uninitialized((qr.num_nodes, self.kernel.valueSize), dtype=REAL) - self.qrId = sQR.qr - self.PHI_id = sQR.PHI3 + self.qrFace = sQR.qr + self.PHI_face = sQR.PHI3 elif panel == COMMON_EDGE: try: sQR = self.specialQuadRules[(singularityValue, panel)] @@ -992,9 +1088,11 @@ cdef class fractionalLaplacian2D_nonsym(fractionalLaplacian2D): qr = singularityCancelationQuadRule2D(panel, self.singularityCancelationIntegrandAcrossElements+singularityValue, + log_singularityValue, self.quad_order_diagonal, self.quad_order_diagonalV, 1) + qr.scaleWeights(4.0) PHI = uninitialized((2*dofs_per_element - 2*dofs_per_vertex - dofs_per_edge, qr.num_nodes, 2), dtype=REAL) @@ -1073,9 +1171,11 @@ cdef class fractionalLaplacian2D_nonsym(fractionalLaplacian2D): qr = singularityCancelationQuadRule2D(panel, self.singularityCancelationIntegrandAcrossElements+singularityValue, + log_singularityValue, self.quad_order_diagonal, self.quad_order_diagonalV, 1) + qr.scaleWeights(4.0) PHI = uninitialized((2*dofs_per_element - dofs_per_vertex, qr.num_nodes, 2), dtype=REAL) @@ -1124,64 +1224,39 @@ cdef class fractionalLaplacian2D_nonsym(fractionalLaplacian2D): REAL_t[:, ::1] contrib, panelType panel, MASK_t mask=ALL): - cdef: - INDEX_t k, m, i, j, I, J, dofs_per_element, dim = self.DoFMap.mesh.dim, l - REAL_t vol, val - quadratureRule qr - REAL_t[:, :, ::1] PHI - REAL_t x[3] - REAL_t y[3] - INDEX_t valueSize = self.kernel.valueSize - if panel >= 1: self.eval_distant_nonsym(contrib, panel, mask) - return - elif panel == COMMON_FACE: - qr = self.qrId - PHI = self.PHI_id - elif panel == COMMON_EDGE: - qr = self.qrEdge - PHI = self.PHI_edge - elif panel == COMMON_VERTEX: - qr = self.qrVertex - PHI = self.PHI_vertex else: - raise NotImplementedError('Unknown panel type: {}'.format(panel)) + self.eval_near_nonsym(contrib, panel, mask) + - vol = 4.0*self.vol1*self.vol2 - - dofs_per_element = self.DoFMap.dofs_per_element - - # Evaluate the kernel on the quadrature nodes - for m in range(qr.num_nodes): - for j in range(dim): - x[j] = (self.simplex1[self.perm1[0], j]*qr.nodes[0, m] + - self.simplex1[self.perm1[1], j]*qr.nodes[1, m] + - self.simplex1[self.perm1[2], j]*qr.nodes[2, m]) - y[j] = (self.simplex2[self.perm2[0], j]*qr.nodes[3, m] + - self.simplex2[self.perm2[1], j]*qr.nodes[4, m] + - self.simplex2[self.perm2[2], j]*qr.nodes[5, m]) - self.kernel.evalPtr(dim, &x[0], &y[0], &self.vec[0]) - self.kernel.evalPtr(dim, &y[0], &x[0], &self.vec2[0]) - for l in range(valueSize): - self.temp[m, l] = qr.weights[m] * self.vec[l] - self.temp2[m, l] = qr.weights[m] * self.vec2[l] - - # "perm" maps from dofs on the reordered simplices (matching - # vertices first) to the dofs in the usual ordering. - contrib[:] = 0. - for I in range(PHI.shape[0]): - i = self.perm[I] - for J in range(PHI.shape[0]): - j = self.perm[J] - k = i*(2*dofs_per_element)+j - # Check if that entry has been requested. - if mask[k]: - for l in range(valueSize): - val = 0. - for m in range(qr.num_nodes): - val += (self.temp[m, l] * PHI[I, m, 0] - self.temp2[m, l] * PHI[I, m, 1]) * (PHI[J, m, 0] - PHI[J, m, 1]) - contrib[k, l] = val*vol +cdef class fractionalLaplacian2D_nonsym2(fractionalLaplacian2D_nonsym): + """The local stiffness matrix + + .. math:: + + 0.5 \\int_{K_1}\\int_{K_2} [ u(x) - u(y) ] [ v(x)-v(y) ] \\gamma(x,y) dy dx + + for the 2D non-symmetric nonlocal Laplacian. + """ + def __init__(self, + Kernel kernel, + meshBase mesh, + DoFMap dm, + target_order=None, + quad_order_diagonal=None, + num_dofs=None, + **kwargs): + super(fractionalLaplacian2D_nonsym2, self).__init__(kernel, mesh, dm, target_order, quad_order_diagonal, num_dofs, **kwargs) + + cdef void eval(self, + REAL_t[:, ::1] contrib, + panelType panel, + MASK_t mask=ALL): + if panel >= 1: + self.eval_distant_nonsym2(contrib, panel, mask) + else: + self.eval_near_nonsym2(contrib, panel, mask) cdef class fractionalLaplacian2D_boundary(fractionalLaplacian2DZeroExterior): @@ -1189,7 +1264,7 @@ cdef class fractionalLaplacian2D_boundary(fractionalLaplacian2DZeroExterior): .. math:: - \\int_{K}\\int_{e} [ u(x) v(x) n_{y} \\cdot \\frac{x-y}{|x-y|} \\Gamma(x,y) dy dx + \\int_{K}\\int_{e} [ u(x) v(x) n_{y} \\cdot \\Gamma(x,y) dy dx for the 2D nonlocal Laplacian. """ @@ -1219,7 +1294,7 @@ cdef class fractionalLaplacian2D_boundary(fractionalLaplacian2DZeroExterior): quad_order_diagonal = max(np.ceil((target_order+0.5+smax)/(0.35)*abs(np.log(self.hmin/self.H0))), 2) self.quad_order_diagonal = quad_order_diagonal - if (self.kernel.kernelType != FRACTIONAL) or (not self.kernel.variableOrder): + if (self.kernel.kernelType != FRACTIONAL) or (not self.kernel.variableSingularity): self.getNearQuadRule(COMMON_EDGE) self.getNearQuadRule(COMMON_VERTEX) @@ -1256,8 +1331,9 @@ cdef class fractionalLaplacian2D_boundary(fractionalLaplacian2DZeroExterior): cdef: INDEX_t i REAL_t singularityValue = self.kernel.getSingularityValue() + REAL_t log_singularityValue = self.kernel.getLogSingularityValue() INDEX_t dof - quadratureRule qr + singularityCancelationQuadRule qr specialQuadRule sQR REAL_t[:, ::1] PHI INDEX_t dofs_per_element = self.DoFMap.dofs_per_element @@ -1269,9 +1345,10 @@ cdef class fractionalLaplacian2D_boundary(fractionalLaplacian2DZeroExterior): sQR = self.specialQuadRules[(singularityValue, panel)] except KeyError: if singularityValue > -2.+1e-3: - qr = singularityCancelationQuadRule2D_boundary(panel, singularityValue, self.quad_order_diagonal, self.quad_order_diagonal) + qr = singularityCancelationQuadRule2D_boundary(panel, singularityValue, log_singularityValue, self.quad_order_diagonal, self.quad_order_diagonal) else: - qr = singularityCancelationQuadRule2D_boundary(panel, 2.+singularityValue, self.quad_order_diagonal, self.quad_order_diagonal) + qr = singularityCancelationQuadRule2D_boundary(panel, 2.+singularityValue, log_singularityValue, self.quad_order_diagonal, self.quad_order_diagonal) + qr.scaleWeights(2.0) PHI = uninitialized((dofs_per_element, qr.num_nodes), dtype=REAL) for dof in range(dofs_per_element): @@ -1294,7 +1371,8 @@ cdef class fractionalLaplacian2D_boundary(fractionalLaplacian2DZeroExterior): try: sQR = self.specialQuadRules[(singularityValue, panel)] except KeyError: - qr = singularityCancelationQuadRule2D_boundary(panel, singularityValue, self.quad_order_diagonal, self.quad_order_diagonal) + qr = singularityCancelationQuadRule2D_boundary(panel, singularityValue, log_singularityValue, self.quad_order_diagonal, self.quad_order_diagonal) + qr.scaleWeights(2.0) PHI = uninitialized((dofs_per_element, qr.num_nodes), dtype=REAL) for dof in range(dofs_per_element): @@ -1325,85 +1403,7 @@ cdef class fractionalLaplacian2D_boundary(fractionalLaplacian2DZeroExterior): REAL_t[:, ::1] contrib, panelType panel, MASK_t mask=ALL): - cdef: - REAL_t vol1 = self.vol1, vol2 = self.vol2, vol, val - INDEX_t i, j, k, I, J, m, l - REAL_t[:, ::1] simplex1 = self.simplex1 - REAL_t[:, ::1] simplex2 = self.simplex2 - REAL_t normW, nw - quadratureRule qr - REAL_t[:, ::1] PHI - INDEX_t dofs_per_element = self.DoFMap.dofs_per_element - INDEX_t dim = self.DoFMap.mesh.dim - REAL_t x[3] - REAL_t y[3] - INDEX_t valueSize = self.kernel.valueSize - if panel >= 1: self.eval_distant_boundary(contrib, panel, mask) - return - - # Kernel: - # \Gamma(x,y) = n \dot (x-y) * C(d,s) / (2s) / |x-y|^{d+2s} - # with inward normal n. - # - # Rewrite as - # \Gamma(x,y) = [ n \dot (x-y)/|x-y| ] * [ C(d,s) / (2s) / |x-y|^{d-1+2s} ] - # \--------------------------------/ - # | - # boundaryKernel - - # n is independent of x and y - self.n[0] = simplex2[1, 1] - simplex2[0, 1] - self.n[1] = simplex2[0, 0] - simplex2[1, 0] - # F is same as vol2 - val = 1./sqrt(mydot(self.n, self.n)) - self.n[0] *= val - self.n[1] *= val - - contrib[:] = 0. - - if panel == COMMON_EDGE: - qr = self.qrEdge - PHI = self.PHI_edge2 - elif panel == COMMON_VERTEX: - qr = self.qrVertex - PHI = self.PHI_vertex2 else: - raise NotImplementedError('Panel type unknown: {}.'.format(panel)) - - vol = -2.0*vol1*vol2 - - for m in range(qr.num_nodes): - normW = 0. - for j in range(dim): - x[j] = (simplex1[self.perm1[0], j]*qr.nodes[0, m] + - simplex1[self.perm1[1], j]*qr.nodes[1, m] + - simplex1[self.perm1[2], j]*qr.nodes[2, m]) - y[j] = (simplex2[self.perm2[0], j]*qr.nodes[3, m] + - simplex2[self.perm2[1], j]*qr.nodes[4, m]) - self.w[j] = x[j]-y[j] - normW += self.w[j]**2 - normW = 1./sqrt(normW) - for j in range(dim): - self.w[j] *= normW - self.kernel.evalPtr(dim, &x[0], &y[0], &self.vec[0]) - nw = mydot(self.n, self.w) - for l in range(valueSize): - self.temp[m, l] = qr.weights[m] * nw * self.vec[l] - for I in range(dofs_per_element): - i = self.perm[I] - for J in range(I, dofs_per_element): - j = self.perm[J] - if j < i: - k = dofs_per_element*j-(j*(j+1) >> 1) + i - else: - k = dofs_per_element*i-(i*(i+1) >> 1) + j - if mask[k]: - for l in range(valueSize): - val = 0. - for m in range(qr.num_nodes): - val += self.temp[m, l] * PHI[I, m] * PHI[J, m] - contrib[k, l] = val*vol - - + self.eval_near_boundary(contrib, panel, mask) diff --git a/nl/PyNucleus_nl/fractionalOrders.pxd b/nl/PyNucleus_nl/fractionalOrders.pxd index e5b2dd87..0218379f 100644 --- a/nl/PyNucleus_nl/fractionalOrders.pxd +++ b/nl/PyNucleus_nl/fractionalOrders.pxd @@ -46,6 +46,11 @@ cdef class singleVariableUnsymmetricFractionalOrder(variableFractionalOrder): cdef REAL_t evalGradPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, INDEX_t vectorSize, REAL_t* grad) +cdef class variableConstFractionalOrder(variableFractionalOrder): + cdef: + public REAL_t value + + cdef class piecewiseConstantFractionalOrder(variableFractionalOrder): cdef: public function blockIndicator diff --git a/nl/PyNucleus_nl/fractionalOrders.pyx b/nl/PyNucleus_nl/fractionalOrders.pyx index cffca408..4325653e 100644 --- a/nl/PyNucleus_nl/fractionalOrders.pyx +++ b/nl/PyNucleus_nl/fractionalOrders.pyx @@ -50,9 +50,6 @@ cdef class fractionalOrderBase(twoPointFunction): self.numParameters = numParameters assert self.numParameters >= 1 - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - raise NotImplementedError() - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): raise NotImplementedError() @@ -74,9 +71,6 @@ cdef class constFractionalOrder(fractionalOrderBase): super(constFractionalOrder, self).__init__(s, s, True) self.value = s - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - value[0] = self.value - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): value[0] = self.value @@ -92,17 +86,15 @@ cdef class constFractionalOrder(fractionalOrderBase): def __reduce__(self): return constFractionalOrder, (self.value, ) + def __eq__(self, other): + return (type(self) == type(other)) and (self.value == other.value) + cdef class variableFractionalOrder(fractionalOrderBase): def __init__(self, REAL_t smin, REAL_t smax, BOOL_t symmetric, INDEX_t numParameters=1): super(variableFractionalOrder, self).__init__(smin, smax, symmetric, numParameters) self.c_params = PyMem_Malloc(NUM_FRAC_ORDER_PARAMS*OFFSET) - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - cdef: - fun_t sFun = getFun(self.c_params, fSFUN) - value[0] = sFun(&x[0], &y[0], self.c_params) - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): cdef: fun_t sFun = getFun(self.c_params, fSFUN) @@ -137,9 +129,6 @@ cdef class singleVariableTwoPointFunction(twoPointFunction): super(singleVariableTwoPointFunction, self).__init__(False, 1) self.fun = fun - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - value[0] = self.fun.eval(x) - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): value[0] = self.fun.evalPtr(dim, x) @@ -155,9 +144,6 @@ cdef class singleVariableUnsymmetricFractionalOrder(variableFractionalOrder): super(singleVariableUnsymmetricFractionalOrder, self).__init__(smin, smax, False, numParameters) self.sFun = sFun - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - value[0] = self.sFun.eval(x) - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): value[0] = self.sFun.evalPtr(dim, x) @@ -201,15 +187,18 @@ cdef REAL_t constFractionalOrderFun(REAL_t *x, REAL_t *y, void *c_params): cdef class variableConstFractionalOrder(variableFractionalOrder): - cdef: - public REAL_t value - def __init__(self, REAL_t s): super(variableConstFractionalOrder, self).__init__(s, s, True) self.value = s setREAL(self.c_params, fSL, self.value) setFun(self.c_params, fSFUN, &constFractionalOrderFun) + cdef void evalGrad(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] grad): + grad[0] = 1. + + cdef REAL_t evalGradPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, INDEX_t vectorSize, REAL_t* grad): + grad[0] = 1. + def __repr__(self): return '{}(s={},sym={})'.format(self.__class__.__name__, self.value, self.symmetric) @@ -383,6 +372,28 @@ cdef class smoothLeftRight(extendedFunction): return self.sr return 0.5*(self.sl+self.sr)+0.5*(self.sr-self.sl)*atan(x[0]*self.slope) * self.fac + cdef void evalGrad(self, REAL_t[::1] x, REAL_t[::1] grad): + if x[0] < -self.r: + grad[0] = 1. + grad[1] = 0. + elif x[0] > self.r: + grad[0] = 0. + grad[1] = 1. + else: + grad[0] = 0.5+0.5*(-1.)*atan(x[0]*self.slope) * self.fac + grad[1] = 0.5+0.5*atan(x[0]*self.slope) * self.fac + + cdef void evalGradPtr(self, INDEX_t dim, REAL_t* x, INDEX_t vectorSize, REAL_t* grad): + if x[0] < -self.r: + grad[0] = 1. + grad[1] = 0. + elif x[0] > self.r: + grad[0] = 0. + grad[1] = 1. + else: + grad[0] = 0.5+0.5*(-1.)*atan(x[0]*self.slope) * self.fac + grad[1] = 0.5+0.5*atan(x[0]*self.slope) * self.fac + def __repr__(self): return '{}(sl={},sr={},r={},slope={})'.format(self.__class__.__name__, self.sl, self.sr, self.r, self.slope) @@ -560,7 +571,7 @@ cdef class lookupExtended(extendedFunction): REAL_t val, val2 INDEX_t cellNo, dof, k cellNo = self.cellFinder.findCell(x) - assert cellNo != -1, "Cannot find a cell for x={}".format(np.array(x)) + assert cellNo != -1, "Cannot find a cell for x={}".format([x[i] for i in range(self.mesh.dim)]) val = 0. for k in range(self.dm.dofs_per_element): dof = self.dm.cell2dof(cellNo, k) @@ -669,6 +680,11 @@ cdef class feFractionalOrder(singleVariableUnsymmetricFractionalOrder): def __reduce__(self): return feFractionalOrder, (self.vec, self.min, self.max) + def __eq__(self, other): + if other is None: + return False + return (type(self) == type(other)) and (np.absolute(self.vec-other.vec).max() < 1e-12) + cdef REAL_t innerOuterFractionalOrderFun(REAL_t *x, REAL_t *y, void *c_params): cdef: @@ -744,11 +760,11 @@ cdef class sumFractionalOrder(variableFractionalOrder): self.s2 = s2 self.fac2 = fac2 - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): + cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): cdef: REAL_t val1, val2 - self.s1.evalPtr(x.shape[0], &x[0], &y[0], &val1) - self.s2.evalPtr(x.shape[0], &x[0], &y[0], &val2) + self.s1.evalPtr(dim, x, y, &val1) + self.s2.evalPtr(dim, x, y, &val2) value[0] = val1*val2 diff --git a/nl/PyNucleus_nl/helpers.py b/nl/PyNucleus_nl/helpers.py index 99a15a43..57634acf 100644 --- a/nl/PyNucleus_nl/helpers.py +++ b/nl/PyNucleus_nl/helpers.py @@ -23,13 +23,12 @@ from PyNucleus_multilevelSolver.connectors import (inputConnector, repartitionConnector) from . twoPointFunctions import constantTwoPoint -from . kernelsCy import FractionalKernel, RangedFractionalKernel +from . kernels import FractionalKernel, RangedFractionalKernel from . nonlocalAssembly import nonlocalBuilder from . operatorInterpolation import (admissibleSet, getChebyIntervalsAndNodes) -from . kernels import getFractionalKernel from . fractionalOrders import constFractionalOrder -from . clusterMethodCy import H2Matrix +from . clusterMethod import H2Matrix import numpy as np from pathlib import Path import h5py @@ -187,7 +186,7 @@ def getFracLapl(DoFMap, kernel=None, rangedOpParams={}, **kwargs): for n in nodes: intervalOps = [] for s in n: - kernel = getFractionalKernel(mesh.dim, constFractionalOrder(s), horizon, scaling=scaling, normalized=normalized) + kernel = FractionalKernel.build(dim=mesh.dim, s=constFractionalOrder(s), horizon=horizon, scaling=scaling, normalized=normalized) intervalOps.append(delayedFractionalLaplacianOp(DoFMap, kernel, **kwargs)) ops.append(intervalOps) A = multiIntervalInterpolationOperator(intervals, nodes, ops) @@ -201,8 +200,8 @@ def getFracLapl(DoFMap, kernel=None, rangedOpParams={}, **kwargs): return A else: horizon = kernel.horizon - scaling = kernel.scaling - normalized = not isinstance(scaling, constantTwoPoint) + # scaling = kernel.scaling + # normalized = not isinstance(scaling, constantTwoPoint) if tag is None or zeroExterior is None: tag, zeroExterior = processBC(tag, boundaryCondition, kernel) @@ -259,7 +258,7 @@ def getFracLapl(DoFMap, kernel=None, rangedOpParams={}, **kwargs): if 'genKernel' in kwargs: params['genKernel'] = kwargs['genKernel'] if kernel is None: - kernel = getFractionalKernel(mesh.dim, s, constant(horizon.ranges[0, 0]), scaling=scaling, normalized=normalized) + kernel = FractionalKernel.build(dim=mesh.dim, s=s, horizon=constant(horizon.ranges[0, 0]), scaling=scaling, normalized=normalized) dm2 = kwargs.pop('dm2', None) if dm2 is not None and matrixFormat.upper() in ('H2', 'SPARSE', 'SPARSIFIED') and DoFMap.num_boundary_dofs > 0: # currently not implemented @@ -267,28 +266,19 @@ def getFracLapl(DoFMap, kernel=None, rangedOpParams={}, **kwargs): A = getFracLapl(dm, kernel, rangedOpParams={}, **kwargs) A = R_interior*A*R_bc.transpose() return A - builder = nonlocalBuilder(DoFMap, kernel, params, zeroExterior=zeroExterior, comm=comm, logging=logging, PLogger=PLogger, dm2=dm2) if diagonal: - with timer('Assemble diagonal matrix {}, zeroExterior={}'.format(kernel, zeroExterior)): - A = builder.getDiagonal() + timerLabel = 'Assemble diagonal matrix {}, zeroExterior={}'.format(kernel, zeroExterior) elif matrixFormat.upper() == 'SPARSE': - with timer('Assemble sparse matrix {}, zeroExterior={}'.format(kernel, zeroExterior)): - A = builder.getSparse() + timerLabel = 'Assemble diagonal matrix {}, zeroExterior={}'.format(kernel, zeroExterior) elif matrixFormat.upper() == 'SPARSIFIED': - with timer('Assemble sparsified matrix {}, zeroExterior={}'.format(kernel, zeroExterior)): - A = builder.getDense(trySparsification=True) + timerLabel = 'Assemble sparsified matrix {}, zeroExterior={}'.format(kernel, zeroExterior) elif matrixFormat.upper() == 'DENSE': - with timer('Assemble dense matrix {}, zeroExterior={}'.format(kernel, zeroExterior)): - if cached: - A = builder.getDenseCached() - else: - A = builder.getDense(trySparsification=trySparsification) + timerLabel = 'Assemble dense matrix {}, zeroExterior={}'.format(kernel, zeroExterior) else: - with timer('Assemble H2 matrix {}, zeroExterior={}'.format(kernel, zeroExterior)): - if isinstance(horizon, constant): - A, Pnear = builder.getH2(returnNearField=True) - else: - A = builder.getH2FiniteHorizon() + timerLabel = 'Assemble H2 matrix {}, zeroExterior={}'.format(kernel, zeroExterior) + with timer(timerLabel): + A = DoFMap.assembleNonlocal(kernel, params=params, zeroExterior=zeroExterior, comm=comm, logging=logging, PLogger=PLogger, dm2=dm2, + trySparsification=trySparsification, matrixFormat=matrixFormat) if doSave and (comm is None or (comm and comm.rank == 0)): if hasattr(A, 'HDF5write'): with timer('Saving'): @@ -466,7 +456,7 @@ def fractionalHierarchy(mesh, s, NoRef, tag=None, eta=3., global_params = {'domain': mesh, 'opType': 'Laplacian', - 'kernel': getFractionalKernel(mesh.dim, s=s, horizon=np.inf), + 'kernel': FractionalKernel.build(dim=mesh.dim, s=s, horizon=np.inf), 'horizon': horizon, 'tag': tag, 'boundaryCondition': boundaryCondition, diff --git a/nl/PyNucleus_nl/interactionDomains.pxd b/nl/PyNucleus_nl/interactionDomains.pxd index c29cc2a5..f020ebaf 100644 --- a/nl/PyNucleus_nl/interactionDomains.pxd +++ b/nl/PyNucleus_nl/interactionDomains.pxd @@ -122,3 +122,9 @@ cdef class ball2_dilation_retriangulation(retriangulationDomain): REAL_t[:, ::1] mat REAL_t c, d REAL_t[:, ::1] tempSimplex1, tempSimplex2 + + +cdef class functionOfDistance(twoPointFunction): + cdef: + interactionDomain interaction + public function fun diff --git a/nl/PyNucleus_nl/interactionDomains.pyx b/nl/PyNucleus_nl/interactionDomains.pyx index 60ef93ff..bb42723a 100644 --- a/nl/PyNucleus_nl/interactionDomains.pyx +++ b/nl/PyNucleus_nl/interactionDomains.pyx @@ -287,7 +287,7 @@ cdef class interactionDomain(parametrizedTwoPointFunction): if self.getRelativePosition(simplex1, simplex2) in (0, 2): self.plot_Simplex(simplex1, simplex2, False) for vertexNo1 in range(simplex1.shape[0]): - self.plot_Surface(simplex2[vertexNo1,:]) + self.plot_Surface(simplex2[vertexNo1, :]) plt.fill(np.concatenate((simplex2[:, 0], [simplex2[0, 0]])), np.concatenate((simplex2[:, 1], [simplex2[0, 1]])), 'b', zorder=1) @@ -834,15 +834,6 @@ cdef class fullSpace(interactionDomain): cdef RELATIVE_POSITION_t getRelativePosition(self, REAL_t[:, ::1] simplex1, REAL_t[:, ::1] simplex2): return INTERACT - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - cdef: - INDEX_t i - REAL_t d2 = 0. - for i in range(x.shape[0]): - d2 += (x[i]-y[i])**2 - self.dist2 = d2 - value[0] = 1. - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): cdef: INDEX_t i @@ -859,6 +850,9 @@ cdef class fullSpace(interactionDomain): def __reduce__(self): return fullSpace, () + def __eq__(self, other): + return isinstance(other, fullSpace) + def getLongDescription(self): return '' @@ -936,19 +930,6 @@ cdef class ball2_retriangulation(retriangulationDomain): numIntersections += 1 return numIntersections - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - cdef: - REAL_t d2 = 0. - INDEX_t i - REAL_t horizon2 = getREAL(self.params, fHORIZON2) - for i in range(x.shape[0]): - d2 += (x[i]-y[i])**2 - self.dist2 = d2 - if d2 <= horizon2: - value[0] = 1. - else: - value[0] = 0. - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): cdef: REAL_t d2 = 0. @@ -1023,19 +1004,6 @@ cdef class ball2_barycenter(barycenterDomain): isInside = (d2 <= horizon2) return isInside - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - cdef: - REAL_t d2 = 0. - INDEX_t i - REAL_t horizon2 = getREAL(self.params, fHORIZON2) - for i in range(x.shape[0]): - d2 += (x[i]-y[i])**2 - self.dist2 = d2 - if d2 <= horizon2: - value[0] = 1. - else: - value[0] = 0. - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): cdef: REAL_t d2 = 0. @@ -1164,19 +1132,6 @@ cdef class ballInf_retriangulation(retriangulationDomain): intersections[0], intersections[1] = intersections[1], intersections[0] return numIntersections - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - cdef: - REAL_t d2 = 0. - INDEX_t i - REAL_t horizon2 = getREAL(self.params, fHORIZON2) - for i in range(x.shape[0]): - d2 = max(d2, (x[i]-y[i])**2) - self.dist2 = d2 - if d2 <= horizon2: - value[0] = 1. - else: - value[0] = 0. - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): cdef: REAL_t d2 = 0. @@ -1266,19 +1221,6 @@ cdef class ballInf_barycenter(barycenterDomain): s = max(s, (x[i]-y[i])**2) return s <= horizon2 - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - cdef: - REAL_t d2 = 0. - INDEX_t i - REAL_t horizon2 = getREAL(self.params, fHORIZON2) - for i in range(x.shape[0]): - d2 = max(d2, (x[i]-y[i])**2) - self.dist2 = d2 - if d2 <= horizon2: - value[0] = 1. - else: - value[0] = 0. - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): cdef: REAL_t d2 = 0. @@ -1357,19 +1299,6 @@ cdef class ball2Complement(retriangulationDomain): self.relPos = CUT return self.relPos - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - cdef: - REAL_t s = 0. - INDEX_t i - REAL_t horizon2 = getREAL(self.params, fHORIZON2) - for i in range(x.shape[0]): - s += (x[i]-y[i])**2 - self.dist2 = s - if s > horizon2: - value[0] = 1. - else: - value[0] = 0. - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): cdef: REAL_t s = 0. @@ -1416,13 +1345,6 @@ cdef class linearTransformInteraction(interactionDomain): self.simplex1 = uninitialized((dim+1, dim), dtype=REAL) self.simplex2 = uninitialized((dim+1, dim), dtype=REAL) - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - if self.callTransform: - self.transform.eval(x, self.A) - self.transformVectorForward(&x[0], &y[0], &self.vec2[0]) - self.baseInteraction.eval(x, self.vec2, value) - self.dist2 = self.baseInteraction.dist2 - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): if self.callTransform: self.transform.evalPtr(dim, x, &self.A[0, 0]) @@ -1549,19 +1471,6 @@ cdef class ellipseTransform(matrixFunction): self.rows = 2 self.columns = 2 - cdef void eval(self, REAL_t[::1] x, REAL_t[:, ::1] vals): - cdef: - REAL_t inv_a = 1./self.a.eval(x) - REAL_t inv_b = 1./self.b.eval(x) - REAL_t theta = self.theta.eval(x) - REAL_t c = cos(theta) - REAL_t s = sin(theta) - - vals[0, 0] = c*inv_a - vals[0, 1] = -s*inv_a - vals[1, 0] = s*inv_b - vals[1, 1] = c*inv_b - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* vals): cdef: REAL_t inv_a = 1./self.a.evalPtr(dim, x) @@ -1636,20 +1545,6 @@ cdef class ball1_retriangulation(linearTransformInteraction): A = np.array([[1., -1.], [1., 1.]], dtype=REAL) super(ball1_retriangulation, self).__init__(horizonFun, base, constantMatrixFunction(A)) - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - cdef: - REAL_t d2 = 0. - INDEX_t i - REAL_t horizon2 = getREAL(self.params, fHORIZON2) - for i in range(x.shape[0]): - d2 += abs(x[i]-y[i]) - d2 *= d2 - self.dist2 = d2 - if d2 <= horizon2: - value[0] = 1. - else: - value[0] = 0. - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): cdef: REAL_t d2 = 0. @@ -1685,20 +1580,6 @@ cdef class ball1_barycenter(linearTransformInteraction): A = np.array([[1., -1.], [1., 1.]], dtype=REAL) super(ball1_barycenter, self).__init__(horizonFun, base, constantMatrixFunction(A)) - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - cdef: - REAL_t d2 = 0. - INDEX_t i - REAL_t horizon2 = getREAL(self.params, fHORIZON2) - for i in range(x.shape[0]): - d2 += abs(x[i]-y[i]) - d2 *= d2 - self.dist2 = d2 - if d2 <= horizon2: - value[0] = 1. - else: - value[0] = 0. - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): cdef: REAL_t d2 = 0. @@ -1805,22 +1686,6 @@ cdef class ball2_dilation_barycenter(barycenterDomain): coeff[i] += self.mat[i, j]*(y[j]-x[j]) return coeff[0]**2+coeff[1]**2 <= horizon2 + min(2*coeff[0]*self.d, 0.) - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - cdef: - INDEX_t i, j - REAL_t horizon2 = getREAL(self.params, fHORIZON2) - REAL_t[2] coeff - - for i in range(x.shape[0]): - coeff[i] = 0. - for j in range(x.shape[0]): - coeff[i] += self.mat[i, j]*(y[j]-x[j]) - - if coeff[0]**2+coeff[1]**2 <= horizon2 + min(2*coeff[0]*self.d, 0.): - value[0] = 1. - else: - value[0] = 0. - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): cdef: INDEX_t i, j @@ -2025,22 +1890,6 @@ cdef class ball2_dilation_retriangulation(retriangulationDomain): numIntersections += 1 return numIntersections - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - cdef: - INDEX_t i, j - REAL_t horizon2 = getREAL(self.params, fHORIZON2) - REAL_t[2] coeff - - for i in range(x.shape[0]): - coeff[i] = 0. - for j in range(x.shape[0]): - coeff[i] += self.mat[i, j]*(y[j]-x[j]) - - if coeff[0]**2+coeff[1]**2 <= horizon2 + min(2*coeff[0]*self.d, 0.): - value[0] = 1. - else: - value[0] = 0. - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): cdef: INDEX_t i, j @@ -2058,7 +1907,7 @@ cdef class ball2_dilation_retriangulation(retriangulationDomain): value[0] = 0. def __repr__(self): - return '|x-y|_2 <= {}'.format(self.horizonFun) + return 'y \\in B(x, {}), x \\in B(y, {})'.format(self.horizonFun, self.horizonFun) def get_Surface(self, REAL_t[::1] node1): cdef: @@ -2103,3 +1952,23 @@ cdef class ball2_dilation_retriangulation(retriangulationDomain): def getLongDescription(self): return '\\chi_{|x-y|_2<\\delta(x)} \\chi_{|x-y|_2<\\delta(y)}' + + +cdef class functionOfDistance(twoPointFunction): + def __init__(self, interaction, function fun): + super(functionOfDistance, self).__init__(True, 1) + self.interaction = interaction + self.fun = fun + + cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): + cdef: + REAL_t d[1] + self.interaction.evalPtr(dim, x, y, value) + d[0] = sqrt(self.interaction.dist2) + value[0] = self.fun.eval(d) + + def __reduce__(self): + return functionOfDistance, (self.interaction, self.fun) + + def __repr__(self): + return repr(self.fun)+'(d(x, y))' diff --git a/nl/PyNucleus_nl/kernelNormalization.pxd b/nl/PyNucleus_nl/kernelNormalization.pxd index 8d93706d..5297504d 100644 --- a/nl/PyNucleus_nl/kernelNormalization.pxd +++ b/nl/PyNucleus_nl/kernelNormalization.pxd @@ -26,43 +26,41 @@ cdef class memoizedFun: cdef class constantFractionalLaplacianScaling(constantTwoPoint): cdef: - INDEX_t dim - REAL_t s, horizon - REAL_t tempered + public INDEX_t dim + public REAL_t s + public REAL_t horizon + public BOOL_t normalized + public BOOL_t boundary + public INDEX_t derivative + public REAL_t tempered + public INDEX_t termNo + public REAL_t[::1] values -cdef class constantFractionalLaplacianScalingBoundary(constantTwoPoint): +cdef class variableFractionalLaplacianScaling(parametrizedTwoPointFunction): cdef: - INDEX_t dim - REAL_t s, horizon, tempered + public INDEX_t dim + public fractionalOrderBase sFun + public function horizonFun + public BOOL_t normalized + public BOOL_t boundary + public INDEX_t derivative + public INDEX_t termNo + public REAL_t[::1] values -cdef class constantFractionalLaplacianScalingDerivative(twoPointFunction): - cdef: - INDEX_t dim - REAL_t s - REAL_t horizon - REAL_t horizon2 - BOOL_t normalized - BOOL_t boundary - INDEX_t derivative - REAL_t tempered - REAL_t C - REAL_t fac - REAL_t fac2 +cdef class variableFractionalLaplacianScalingWithDifferentHorizon(variableFractionalLaplacianScaling): + pass -cdef class variableFractionalLaplacianScaling(parametrizedTwoPointFunction): +cdef class constantIntegrableScaling(constantTwoPoint): cdef: + kernelType kType INDEX_t dim - fractionalOrderBase sFun - function horizonFun - REAL_t facInfinite, facFinite - twoPointFunction phi - BOOL_t normalized - BOOL_t boundary - INDEX_t derivative - memoizedFun digamma + REAL_t horizon + interactionDomain interaction + REAL_t gaussian_variance + REAL_t exponentialRate cdef class variableIntegrableScaling(parametrizedTwoPointFunction): @@ -74,31 +72,5 @@ cdef class variableIntegrableScaling(parametrizedTwoPointFunction): twoPointFunction phi -cdef class variableFractionalLaplacianScalingBoundary(parametrizedTwoPointFunction): - cdef: - INDEX_t dim - fractionalOrderBase sFun - function horizonFun - REAL_t facInfinite, facFinite - twoPointFunction phi - BOOL_t normalized - - -cdef class variableFractionalLaplacianScalingWithDifferentHorizon(variableFractionalLaplacianScaling): - pass - - cdef class variableIntegrableScalingWithDifferentHorizon(variableIntegrableScaling): pass - - -cdef class constantIntegrableScaling(constantTwoPoint): - cdef: - kernelType kType - INDEX_t dim - REAL_t horizon - interactionDomain interaction - REAL_t gaussian_variance - REAL_t exponentialRate - - diff --git a/nl/PyNucleus_nl/kernelNormalization.pyx b/nl/PyNucleus_nl/kernelNormalization.pyx index c7dc9c97..07a1ab31 100644 --- a/nl/PyNucleus_nl/kernelNormalization.pyx +++ b/nl/PyNucleus_nl/kernelNormalization.pyx @@ -14,8 +14,9 @@ from libc.math cimport (sqrt, exp, erf) from scipy.special.cython_special cimport psi as digamma from scipy.special.cython_special cimport gamma as cgamma -from scipy.special import zeta - +from . zeta cimport zetaCy as zeta +from PyNucleus_base.myTypes import REAL +from PyNucleus_base.blas import uninitialized from PyNucleus_fem.functions cimport constant from cpython.mem cimport PyMem_Malloc from libc.string cimport memcpy @@ -29,11 +30,11 @@ include "kernel_params.pxi" cdef REAL_t inf = np.inf -cdef inline REAL_t gamma(REAL_t d): +cdef inline REAL_t gamma(REAL_t d) noexcept: return cgamma(d) -cdef inline REAL_t polygamma(INDEX_t n, REAL_t d): +cdef inline REAL_t polygamma(INDEX_t n, REAL_t d) noexcept: return (-1.0)**(n+1) * cgamma(n+1.0) * zeta(n+1, d) @@ -53,6 +54,20 @@ cdef class memoizedFun: print(len(self.memory), self.hit, self.miss) +cdef class memoizedGamma(memoizedFun): + cdef REAL_t eval(self, REAL_t x): + cdef REAL_t val + try: + val = self.memory[x] + self.hit += 1 + return val + except KeyError: + self.miss += 1 + val = gamma(x) + self.memory[x] = val + return val + + cdef class memoizedDigamma(memoizedFun): cdef REAL_t eval(self, REAL_t x): cdef REAL_t val @@ -67,470 +82,371 @@ cdef class memoizedDigamma(memoizedFun): return val -cdef class constantFractionalLaplacianScaling(constantTwoPoint): - def __init__(self, INDEX_t dim, REAL_t s, REAL_t horizon, REAL_t tempered): - self.dim = dim - if 1. < s and s < 2.: - s = s-1. - self.s = s - self.horizon = horizon - self.tempered = tempered - if (self.horizon <= 0.) or (self.s <= 0.) or (self.s >= 1.): - value = np.nan - else: - if horizon < inf: - value = (2.-2*s) * pow(horizon, 2*s-2.) * dim * gamma(0.5*dim)/pow(pi, 0.5*dim) * 0.5 - else: - if (tempered == 0.) or (s == 0.5): - value = 2.0**(2.0*s) * s * gamma(s+0.5*dim)/pow(pi, 0.5*dim)/gamma(1.0-s) * 0.5 - else: - value = gamma(0.5*dim) / abs(gamma(-2*s))/pow(pi, 0.5*dim) * 0.5 * 0.5 - super(constantFractionalLaplacianScaling, self).__init__(value) +cdef class memoizedPolygamma1(memoizedFun): + cdef REAL_t eval(self, REAL_t x): + cdef REAL_t val + try: + val = self.memory[x] + self.hit += 1 + return val + except KeyError: + self.miss += 1 + val = polygamma(1, x) + self.memory[x] = val + return val - def __reduce__(self): - return constantFractionalLaplacianScaling, (self.dim, self.s, self.horizon, self.tempered) - def __repr__(self): - return '{}({},{} -> {})'.format(self.__class__.__name__, self.s, self.horizon, self.value) +cdef: + memoizedDigamma mem_digamma = memoizedDigamma() + memoizedPolygamma1 mem_polygamma = memoizedPolygamma1() - def getLongDescription(self): - if self.horizon < inf: - descr = '\\frac{(2-2s) horizon^{2s-2} d \\Gamma(d/2)}{\\pi^{d/2}}' - else: - if (self.tempered == 0.) or (self.s == 0.5): - descr = '\\frac{2^{2s} s \\Gamma(s+d/2)}{\\pi^{d/2} \\Gamma(1-s)}' - else: - descr = '\\frac{\\Gamma(d/2)}{2 |\\Gamma(-2s)| \\pi^{d/2}}' - return descr +###################################################################### -cdef class constantFractionalLaplacianScalingDerivative(twoPointFunction): - def __init__(self, INDEX_t dim, REAL_t s, REAL_t horizon, BOOL_t normalized, BOOL_t boundary, INDEX_t derivative, REAL_t tempered): - super(constantFractionalLaplacianScalingDerivative, self).__init__(True, 1) - - self.dim = dim - self.s = s - self.horizon = horizon - self.normalized = normalized - self.boundary = boundary - self.derivative = derivative - self.tempered = tempered +cdef inline void fractionalLaplacianScaling(INDEX_t dim, REAL_t s, REAL_t horizon2, BOOL_t normalized, BOOL_t boundary, INDEX_t derivative, REAL_t tempered, REAL_t* values): + cdef: + REAL_t C, d2, logd2, loghorizon2, digamma1, digamma2, fac, fac2, log4 + INDEX_t i + + if normalized: + if horizon2 < inf: + if dim == 1: + C = (2.-2*s) * pow(horizon2, s-1.) + elif dim == 2: + C = (2.-2*s) * pow(horizon2, s-1.) * 2./pi + elif dim == 3: + C = (2.-2*s) * pow(horizon2, s-1.) * 1.5/pi + else: + raise NotImplementedError() + else: + C = 2.0**(2.0*s) * s * gamma(s+0.5*dim) * pow(pi, -0.5*dim) / gamma(1.0-s) + else: + C = 1. - horizon2 = horizon**2 - self.horizon2 = horizon2 + if boundary: + C /= (-2.*s) - if self.normalized: + if derivative == 0: + values[0] = C + elif derivative == 1: + if normalized: if horizon2 < inf: - self.C = (2.-2*s) * pow(horizon2, s-1.) * dim*gamma(0.5*dim)/pow(pi, 0.5*dim) * 0.5 + if not boundary: + fac = -1./(1.-s) + log(horizon2) + else: + fac = -1./(1.-s) - 1./s + log(horizon2) else: - if (tempered == 0.) or (s == 0.5): - self.C = 2.0**(2.0*s) * s * gamma(s+0.5*dim) * pow(pi, -0.5*dim) / gamma(1.0-s) * 0.5 + if not boundary: + fac = log(4.)+mem_digamma.eval(s+0.5*dim) + mem_digamma.eval(-s) else: - self.C = gamma(0.5*dim) / abs(gamma(-2*s))/pow(pi, 0.5*dim) * 0.5 * 0.5 + fac = log(4.)+mem_digamma.eval(s+0.5*dim) + mem_digamma.eval(1.-s) else: - self.C = 0.5 - - if self.derivative == 1: - if self.normalized: - if horizon2 < inf: - if not self.boundary: - self.fac = -1./(1.-s) + log(horizon2) - else: - self.fac = -1./(1.-s) - 1./s + log(horizon2) + if not boundary: + fac = 0. + else: + fac = -1./s + values[0] = C*fac + values[1] = 2.*C + elif derivative == 2: + if normalized: + if horizon2 < inf: + if not boundary: + fac = -2./(1-s) * log(horizon2) + log(horizon2)**2 + fac2 = -2.*(-1./(1.-s) + log(horizon2)) else: - if not self.boundary: - self.fac = log(4.)+digamma(s+0.5*self.dim) + digamma(-s) - else: - self.fac = log(4.)+digamma(s+0.5*self.dim) + digamma(1.-s) + fac = -2./(1-s) * log(horizon2) + log(horizon2)**2 + 2./s**2 - 2./s * (-1./(1.-s) + log(horizon2)) + fac2 = -2.*(-1./(1.-s) + log(horizon2) - 1./s) else: - if not self.boundary: - self.fac = 0. + if not boundary: + fac = (log(4.)+mem_digamma.eval(s+0.5*dim) + mem_digamma.eval(-s))**2 + (mem_polygamma.eval(s+0.5*dim) - mem_polygamma.eval(-s)) + fac2 = -2*(log(4.)+mem_digamma(s+0.5*dim) + mem_digamma(-s)) else: - self.fac = -1./s - elif self.derivative == 2: - if self.normalized: - if horizon2 < inf: - if not self.boundary: - self.fac = -2./(1-s) * log(horizon2) + log(horizon2)**2 - self.fac2 = -2.*(-1./(1.-s) + log(horizon2)) - else: - self.fac = -2./(1-s) * log(horizon2) + log(horizon2)**2 + 2./s**2 - 2./s * (-1./(1.-s) + log(horizon2)) - self.fac2 = -2.*(-1./(1.-s) + log(horizon2) - 1./s) + fac = (log(4.)+mem_digamma.eval(s+0.5*dim) + mem_digamma.eval(1.-s))**2 + (mem_polygamma.eval(s+0.5*dim) - mem_polygamma.eval(1.-s)) + fac2 = -2.*(log(4.)+mem_digamma.eval(s+0.5*dim) + mem_digamma.eval(1.-s)) + else: + if not boundary: + fac = 0. + fac2 = 0. + else: + fac = 2./s**2 + fac2 = 2./s + values[0] = C*fac + values[1] = -2.0*C*fac2 + values[2] = 4.*C + else: + raise NotImplementedError(derivative) + + +cdef inline str fractionalLaplacianScalingDescription(INDEX_t dim, REAL_t s, REAL_t horizon2, BOOL_t normalized, BOOL_t boundary, INDEX_t derivative, REAL_t tempered, INDEX_t termNo): + if normalized: + if horizon2 < inf: + descr = '\\frac{(2-2s) horizon^{2s-2} d \\Gamma(d/2)}{\\pi^{d/2}}' + else: + if (tempered == 0.) or (s == 0.5): + descr = '\\frac{2^{2s} s \\Gamma(s+d/2)}{\\pi^{d/2} \\Gamma(1-s)}' + else: + descr = '\\frac{\\Gamma(d/2)}{2 |\\Gamma(-2s)| \\pi^{d/2}}' + else: + descr = '' + if derivative == 0: + return descr + elif derivative == 1: + if normalized: + if horizon2 < inf: + if not boundary: + fac = '(-\\frac{-1}{1-s} + \\log(horizon^{2}))' else: - if not self.boundary: - self.fac = (log(4.)+digamma(s+0.5*self.dim) + digamma(-s))**2 + (polygamma(1, s+0.5*dim) - polygamma(1, -s)) - self.fac2 = -2*(log(4.)+digamma(s+0.5*self.dim) + digamma(-s)) - else: - self.fac = (log(4.)+digamma(s+0.5*self.dim) + digamma(1.-s))**2 + (polygamma(1, s+0.5*dim) - polygamma(1, 1.-s)) - self.fac2 = -2.*(log(4.)+digamma(s+0.5*self.dim) + digamma(1.-s)) + fac = '(-\\frac{-1}{1-s} - \\frac{1}{s} + \\log(horizon^{2}))' else: - if not self.boundary: - self.fac = 0. - self.fac2 = 0. + if not boundary: + fac = '(\\log 4 + \\psi(s+d/2) + \\psi(-s))' else: - self.fac = 2./s**2 - self.fac2 = 2./s + fac = '(\\log 4 + \\psi(s+d/2) + \\psi(1-s))' else: - raise NotImplementedError(self.derivative) - - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - cdef: - REAL_t d2, logd2 - INDEX_t i - - if self.derivative == 0: - value[0] = self.C - elif self.derivative == 1: - d2 = 0. - for i in range(self.dim): - d2 += (x[i]-y[i])*(x[i]-y[i]) - value[0] = self.C*(-log(d2) + self.fac) - elif self.derivative == 2: - d2 = 0. - for i in range(self.dim): - d2 += (x[i]-y[i])*(x[i]-y[i]) - logd2 = log(d2) - value[0] = self.C*(logd2**2 + self.fac + self.fac2*logd2) - else: - raise NotImplementedError() - - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): - cdef: - REAL_t d2, logd2 - INDEX_t i - - if self.derivative == 0: - value[0] = self.C - elif self.derivative == 1: - d2 = 0. - for i in range(self.dim): - d2 += (x[i]-y[i])*(x[i]-y[i]) - value[0] = self.C*(-log(d2) + self.fac) - elif self.derivative == 2: - d2 = 0. - for i in range(self.dim): - d2 += (x[i]-y[i])*(x[i]-y[i]) - logd2 = log(d2) - value[0] = self.C*(logd2**2 + self.fac + self.fac2*logd2) + if not boundary: + fac = '0' + else: + fac = '(\\frac{1}{2s^2})' + if termNo == 0: + return fac + ' * '+descr + elif termNo == 1: + return '2'+descr else: raise NotImplementedError() - - def __reduce__(self): - return constantFractionalLaplacianScalingDerivative, (self.dim, self.s, self.horizon, self.normalized, self.boundary, self.derivative, self.tempered) - - def __repr__(self): - return "{}({},{} -> {})".format(self.__class__.__name__, self.s, self.horizon, self.fac) + elif derivative == 2: + return 'MISSING'+descr + else: + raise NotImplementedError() -cdef class constantIntegrableScaling(constantTwoPoint): - def __init__(self, kernelType kType, interactionDomain interaction, INDEX_t dim, REAL_t horizon, REAL_t gaussian_variance=1.0, REAL_t exponentialRate=1.0): - self.kType = kType +cdef class constantFractionalLaplacianScaling(constantTwoPoint): + def __init__(self, INDEX_t dim, REAL_t s, REAL_t horizon, BOOL_t normalized, BOOL_t boundary, INDEX_t derivative, REAL_t tempered, INDEX_t termNo): + cdef: + REAL_t C, fac, fac2 self.dim = dim - self.interaction = interaction + self.s = s self.horizon = horizon - self.gaussian_variance = gaussian_variance - self.exponentialRate = exponentialRate - if self.horizon <= 0.: - value = np.nan + self.normalized = normalized + self.boundary = boundary + self.derivative = derivative + self.tempered = tempered + self.termNo = termNo + self.values = uninitialized((self.derivative+1), dtype=REAL) + assert 0 <= self.termNo <= self.derivative + + horizon2 = horizon**2 + fractionalLaplacianScaling(dim, s, horizon2, normalized, boundary, derivative, tempered, &self.values[0]) + if self.derivative > 0: + value = 1. else: - if kType == INDICATOR: - if dim == 1: - value = 3./horizon**3 / 2. - elif dim == 2: - if isinstance(self.interaction, (ball2_retriangulation, ball2_barycenter, ellipse_retriangulation, ellipse_barycenter)): - value = 8./pi/horizon**4 / 2. - elif isinstance(self.interaction, (ballInf_retriangulation, ballInf_barycenter)): - value = 3./4./horizon**4 / 2. - else: - raise NotImplementedError() - else: - raise NotImplementedError() - elif kType == PERIDYNAMIC: - if dim == 1: - value = 2./horizon**2 / 2. - elif dim == 2: - if isinstance(self.interaction, (ball2_retriangulation, ball2_barycenter, ellipse_retriangulation, ellipse_barycenter)): - value = 6./pi/horizon**3 / 2. - else: - raise NotImplementedError() - else: - raise NotImplementedError() - elif kType == GAUSSIAN: - if dim == 1: - if horizon < inf: - # value = 4.0/sqrt(pi)/(horizon/3.)**3 / 2. - value = 4.0/sqrt(pi)/(erf(3.0)-6.0*exp(-9.0)/sqrt(pi))/(horizon/3.0)**3 / 2. - else: - value = 1.0/sqrt(2.0*pi*gaussian_variance) / 2. - elif dim == 2: - if isinstance(self.interaction, (ball2_retriangulation, ball2_barycenter, ellipse_retriangulation, ellipse_barycenter)): - # value = 4.0/pi/(horizon/3.0)**4 / 2. - value = 4.0/pi/(1.0-10.0*exp(-9.0))/(horizon/3.0)**4 / 2. - elif isinstance(self.interaction, fullSpace): - value = 1.0/(2.0*pi*gaussian_variance) / 2. - else: - raise NotImplementedError() - else: - raise NotImplementedError() - elif kType == EXPONENTIAL: - if dim == 1: - if horizon < inf: - value = exponentialRate**3/(2.0-exp(-exponentialRate*horizon)*(2.0 + 2.0*exponentialRate*horizon + (exponentialRate*horizon)**2)) / 2. - else: - value = exponentialRate**3/2.0 / 2. - else: - raise NotImplementedError() - elif kType == POLYNOMIAL: - value = 0.5 - else: - raise NotImplementedError() - super(constantIntegrableScaling, self).__init__(value) + value = self.values[self.termNo] + super(constantFractionalLaplacianScaling, self).__init__(value) def __reduce__(self): - return constantIntegrableScaling, (self.kType, self.interaction, self.dim, self.horizon, self.gaussian_variance, self.exponentialRate) + return constantFractionalLaplacianScaling, (self.dim, self.s, self.horizon, self.normalized, self.boundary, self.derivative, self.tempered, self.termNo) - def __repr__(self): - return '{}({} -> {})'.format(self.__class__.__name__, self.horizon, self.value) + def __eq__(self, constantFractionalLaplacianScaling other): + return (self.dim == other.dim) and (self.s == other.s) and (self.horizon == other.horizon) and (self.boundary == other.boundary) and (self.derivative == other.derivative) and (self.tempered == other.tempered) and (self.termNo == other.termNo) def getLongDescription(self): - descr = '' - if self.kType == INDICATOR: - if self.dim == 1: - descr = '\\frac{3}{\\delta^3}' - elif self.dim == 2: - if isinstance(self.interaction, (ball2_retriangulation, ball2_barycenter, ellipse_retriangulation, ellipse_barycenter)): - descr = '\\frac{8}{\\pi\\delta^4}' - elif isinstance(self.interaction, (ballInf_retriangulation, ballInf_barycenter)): - descr = '\\frac{3}{4\\delta^4}' - elif self.kType == PERIDYNAMIC: - if self.dim == 1: - descr = '\\frac{2}{\\delta^2}' - if self.dim == 2: - if isinstance(self.interaction, (ball2_retriangulation, ball2_barycenter, ellipse_retriangulation, ellipse_barycenter)): - descr = '\\frac{6}{\\pi\\delta^3}' - elif self.kType == GAUSSIAN: - if self.horizon < inf: - if self.dim == 1: - descr = '\\frac{4}{\\sqrt(\\pi) (\\operatorname{erf}(3)-6\\exp(-9)/\\sqrt(\\pi)) (\\delta/3)^3}' - elif self.dim == 2: - descr = '\\frac{4.0}{\\pi (1-10\\exp(-9)) (\\delta/3)^4}' - else: - descr = '\\frac{1}{(2\\pi\\sigma)^{d/2}}' - elif self.kType == EXPONENTIAL: - if self.horizon < inf: - descr = '\\frac{a^3}{2-exp(-a\\delta) (2+2a\\delta + (a\\delta)^2)}' - else: - descr = '\\frac{a^3}{2}' - elif self.kType == POLYNOMIAL: - descr = '' - return descr + return fractionalLaplacianScalingDescription(self.dim, self.s, self.horizon**2, self.normalized, self.boundary, self.derivative, self.tempered, self.termNo) + + def __repr__(self): + return '{}'.format(self.values[self.termNo]) cdef class variableFractionalLaplacianScaling(parametrizedTwoPointFunction): - def __init__(self, BOOL_t symmetric, BOOL_t normalized=True, BOOL_t boundary=False, INDEX_t derivative=0): + def __init__(self, BOOL_t symmetric, BOOL_t normalized, BOOL_t boundary, INDEX_t derivative, INDEX_t termNo): super(variableFractionalLaplacianScaling, self).__init__(symmetric, 1) self.normalized = normalized self.boundary = boundary self.derivative = derivative - self.digamma = memoizedDigamma() + self.termNo = termNo + self.values = uninitialized((self.derivative+1), dtype=REAL) + assert 0 <= self.termNo <= self.derivative cdef void setParams(self, void *params): parametrizedTwoPointFunction.setParams(self, params) self.dim = getINDEX(self.params, fKDIM) - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): + cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): cdef: REAL_t s = getREAL(self.params, fS) REAL_t horizon2 = getREAL(self.params, fHORIZON2) - REAL_t C, d2, fac, fac2 - INDEX_t i - - if self.normalized: - if horizon2 < inf: - if self.dim == 1: - C = (2.-2*s) * pow(horizon2, s-1.) * 0.5 - elif self.dim == 2: - C = (2.-2*s) * pow(horizon2, s-1.) * 2./pi * 0.5 - elif self.dim == 3: - C = (2.-2*s) * pow(horizon2, s-1.) * 1.5/pi * 0.5 - else: - raise NotImplementedError() - else: - C = 2.0**(2.0*s) * s * gamma(s+0.5*self.dim) * pow(pi, -0.5*self.dim) / gamma(1.0-s) * 0.5 + fractionalLaplacianScaling(self.dim, s, horizon2, self.normalized, self.boundary, self.derivative, 0., &self.values[0]) + if self.derivative > 0: + value[0] = 1. else: - C = 0.5 - - if self.derivative == 0: - value[0] = C - elif self.derivative == 1: - d2 = 0. - for i in range(self.dim): - d2 += (x[i]-y[i])*(x[i]-y[i]) - if self.normalized: - if horizon2 < inf: - if not self.boundary: - value[0] = C*(-log(d2/horizon2) - 1./(1.-s)) - else: - value[0] = C*(-log(d2/horizon2) - 1./(1.-s) - 1./s) - else: - if not self.boundary: - value[0] = C*(-log(0.25*d2) + self.digamma.eval(s+0.5*self.dim) + self.digamma.eval(-s)) - else: - value[0] = C*(-log(0.25*d2) + self.digamma.eval(s+0.5*self.dim) + self.digamma.eval(1.-s)) - else: - if not self.boundary: - value[0] = C*(-log(d2)) - else: - value[0] = C*(-log(d2)-1./s) - elif self.derivative == 2: - d2 = 0. - for i in range(self.dim): - d2 += (x[i]-y[i])*(x[i]-y[i]) - logd2 = log(d2) - if self.normalized: - if horizon2 < inf: - loghorizon2 = log(horizon2) - if not self.boundary: - fac = -2./(1-s) * loghorizon2 + loghorizon2**2 - fac2 = -2.*(-1./(1.-s) + log(horizon2)) - else: - fac = -2./(1-s) * loghorizon2 + loghorizon2**2 + 2./s**2 - 2./s * (-1./(1.-s) + loghorizon2) - fac2 = -2.*(-1./(1.-s) + loghorizon2 - 1./s) - else: - digamma1 = self.digamma(s+0.5*self.dim) - if not self.boundary: - digamma2 = self.digamma(-s) - fac = (log(4.)+digamma1 + digamma2)**2 + (polygamma(1, s+0.5*self.dim) - polygamma(1, -s)) - fac2 = -2*(log(4.)+digamma1 + digamma2) - else: - digamma2 = self.digamma(1.-s) - fac = (log(4.)+digamma1 + digamma2)**2 + (polygamma(1, s+0.5*self.dim) - polygamma(1, 1.-s)) - fac2 = -2.*(log(4.)+digamma1 + digamma2) - else: - if not self.boundary: - fac = 0. - fac2 = 0. - else: - fac = 2./s**2 - fac2 = 2./s + value[0] = self.values[self.termNo] - value[0] = C*(logd2**2 + fac + fac2*logd2) + def getScalingWithDifferentHorizon(self): + cdef: + variableFractionalLaplacianScalingWithDifferentHorizon scaling + function horizonFun + BOOL_t horizonFunNull = isNull(self.params, fHORIZONFUN) + if not horizonFunNull: + horizonFun = (((self.params+fHORIZONFUN))[0]) else: - raise NotImplementedError() + horizonFun = constant(sqrt(getREAL(self.params, fHORIZON2))) + scaling = variableFractionalLaplacianScalingWithDifferentHorizon(self.symmetric, self.normalized, self.boundary, self.derivative, self.termNo, horizonFun) + return scaling - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): + def __repr__(self): + return 'variableFractionalLaplacianScaling(symmetric={},normalized={},boundary={},derivative={},termNo={})'.format(self.symmetric, self.normalized, self.boundary, self.derivative, self.termNo) + + def __reduce__(self): + return variableFractionalLaplacianScaling, (self.symmetric, self.normalized, self.boundary, self.derivative, self.termNo) + + def __eq__(self, variableFractionalLaplacianScaling other): + return (self.symmetric == other.symmetric) and (self.normalized == other.normalized) and (self.boundary == other.boundary) and (self.derivative == other.derivative) and (self.termNo == other.termNo) + + def getLongDescription(self): cdef: REAL_t s = getREAL(self.params, fS) REAL_t horizon2 = getREAL(self.params, fHORIZON2) - REAL_t C, d2, logd2, loghorizon2, digamma1, digamma2 - INDEX_t i + return fractionalLaplacianScalingDescription(self.dim, s, horizon2, self.normalized, self.boundary, self.derivative, 0., self.termNo) - if self.normalized: - if horizon2 < inf: - if self.dim == 1: - C = (2.-2*s) * pow(horizon2, s-1.) * 0.5 - elif self.dim == 2: - C = (2.-2*s) * pow(horizon2, s-1.) * 2./pi * 0.5 - elif self.dim == 3: - C = (2.-2*s) * pow(horizon2, s-1.) * 1.5/pi * 0.5 + +cdef class variableFractionalLaplacianScalingWithDifferentHorizon(variableFractionalLaplacianScaling): + def __init__(self, BOOL_t symmetric, BOOL_t normalized, BOOL_t boundary, INDEX_t derivative, INDEX_t termNo, function horizonFun): + super(variableFractionalLaplacianScalingWithDifferentHorizon, self).__init__(symmetric, normalized, boundary, derivative, termNo) + self.horizonFun = horizonFun + + cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): + cdef: + void* params + void* paramsModified = PyMem_Malloc(NUM_KERNEL_PARAMS*OFFSET) + REAL_t horizon, scalingValue + REAL_t[::1] xA = x + horizon = self.horizonFun.eval(xA) + params = self.getParams() + memcpy(paramsModified, params, NUM_KERNEL_PARAMS*OFFSET) + setREAL(paramsModified, fHORIZON2, horizon**2) + self.setParams(paramsModified) + variableFractionalLaplacianScaling.evalPtr(self, dim, x, y, &scalingValue) + self.setParams(params) + value[0] = scalingValue + + def __reduce__(self): + return variableFractionalLaplacianScalingWithDifferentHorizon, (self.symmetric, self.normalized, self.boundary, self.derivative, self.termNo, self.horizonFun) + + +###################################################################### + +cdef inline REAL_t integrableScaling(kernelType kType, interactionDomain interaction, INDEX_t dim, REAL_t horizon, REAL_t gaussian_variance, REAL_t exponentialRate): + cdef: + REAL_t value + if horizon <= 0.: + value = np.nan + else: + if kType == INDICATOR: + if dim == 1: + value = 3./horizon**3 + elif dim == 2: + if isinstance(interaction, (ball2_retriangulation, ball2_barycenter, ellipse_retriangulation, ellipse_barycenter)): + value = 8./pi/horizon**4 + elif isinstance(interaction, (ballInf_retriangulation, ballInf_barycenter)): + value = 3./4./horizon**4 else: raise NotImplementedError() else: - C = 2.0**(2.0*s) * s * gamma(s+0.5*self.dim) * pow(pi, -0.5*self.dim) / gamma(1.0-s) * 0.5 - else: - C = 0.5 - - if self.derivative == 0: - value[0] = C - elif self.derivative == 1: - d2 = 0. - for i in range(self.dim): - d2 += (x[i]-y[i])*(x[i]-y[i]) - if self.normalized: - if horizon2 < inf: - if not self.boundary: - value[0] = C*(-log(d2/horizon2) - 1./(1.-s)) - else: - value[0] = C*(-log(d2/horizon2) - 1./(1.-s) - 1./s) + raise NotImplementedError() + elif kType == PERIDYNAMIC: + if dim == 1: + value = 2./horizon**2 + elif dim == 2: + if isinstance(interaction, (ball2_retriangulation, ball2_barycenter, ellipse_retriangulation, ellipse_barycenter)): + value = 6./pi/horizon**3 else: - if not self.boundary: - value[0] = C*(-log(0.25*d2) + self.digamma.eval(s+0.5*self.dim) + self.digamma.eval(-s)) - else: - value[0] = C*(-log(0.25*d2) + self.digamma.eval(s+0.5*self.dim) + self.digamma.eval(1.-s)) + raise NotImplementedError() else: - if not self.boundary: - value[0] = C*(-log(d2)) + raise NotImplementedError() + elif kType == GAUSSIAN: + if dim == 1: + if horizon < inf: + # value = 4.0/sqrt(pi)/(horizon/3.)**3 / 2. + value = 4.0/sqrt(pi)/(erf(3.0)-6.0*exp(-9.0)/sqrt(pi))/(horizon/3.0)**3 else: - value[0] = C*(-log(d2)-1./s) - elif self.derivative == 2: - d2 = 0. - for i in range(self.dim): - d2 += (x[i]-y[i])*(x[i]-y[i]) - logd2 = log(d2) - if self.normalized: - if horizon2 < inf: - loghorizon2 = log(horizon2) - if not self.boundary: - fac = -2./(1-s) * loghorizon2 + loghorizon2**2 - fac2 = -2.*(-1./(1.-s) + log(horizon2)) - else: - fac = -2./(1-s) * loghorizon2 + loghorizon2**2 + 2./s**2 - 2./s * (-1./(1.-s) + loghorizon2) - fac2 = -2.*(-1./(1.-s) + loghorizon2 - 1./s) + value = 1.0/sqrt(2.0*pi*gaussian_variance) + elif dim == 2: + if isinstance(interaction, (ball2_retriangulation, ball2_barycenter, ellipse_retriangulation, ellipse_barycenter)): + # value = 4.0/pi/(horizon/3.0)**4 + value = 4.0/pi/(1.0-10.0*exp(-9.0))/(horizon/3.0)**4 + elif isinstance(interaction, fullSpace): + value = 1.0/(2.0*pi*gaussian_variance) else: - digamma1 = self.digamma(s+0.5*self.dim) - if not self.boundary: - digamma2 = self.digamma(-s) - fac = (log(4.)+digamma1 + digamma2)**2 + (polygamma(1, s+0.5*self.dim) - polygamma(1, -s)) - fac2 = -2*(log(4.)+digamma1 + digamma2) - else: - digamma2 = self.digamma(1.-s) - fac = (log(4.)+digamma1 + digamma2)**2 + (polygamma(1, s+0.5*self.dim) - polygamma(1, 1.-s)) - fac2 = -2.*(log(4.)+digamma1 + digamma2) + raise NotImplementedError() else: - if not self.boundary: - fac = 0. - fac2 = 0. + raise NotImplementedError() + elif kType == EXPONENTIAL: + if dim == 1: + if horizon < inf: + value = exponentialRate**3/(2.0-exp(-exponentialRate*horizon)*(2.0 + 2.0*exponentialRate*horizon + (exponentialRate*horizon)**2)) else: - fac = 2./s**2 - fac2 = 2./s - - value[0] = C*(logd2**2 + fac + fac2*logd2) + value = exponentialRate**3/2.0 + else: + raise NotImplementedError() + elif kType == POLYNOMIAL: + value = 1.0 else: raise NotImplementedError() - - def getScalingWithDifferentHorizon(self): - cdef: - variableFractionalLaplacianScalingWithDifferentHorizon scaling - function horizonFun - BOOL_t horizonFunNull = isNull(self.params, fHORIZONFUN) - if not horizonFunNull: - horizonFun = (((self.params+fHORIZONFUN))[0]) + return value + + +cdef inline str integrableScalingDescription(kernelType kType, interactionDomain interaction, INDEX_t dim, REAL_t horizon, REAL_t gaussian_variance, REAL_t exponentialRate): + descr = '' + if kType == INDICATOR: + if dim == 1: + descr = '\\frac{3}{\\delta^3}' + elif dim == 2: + if isinstance(interaction, (ball2_retriangulation, ball2_barycenter, ellipse_retriangulation, ellipse_barycenter)): + descr = '\\frac{8}{\\pi\\delta^4}' + elif isinstance(interaction, (ballInf_retriangulation, ballInf_barycenter)): + descr = '\\frac{3}{4\\delta^4}' + elif kType == PERIDYNAMIC: + if dim == 1: + descr = '\\frac{2}{\\delta^2}' + if dim == 2: + if isinstance(interaction, (ball2_retriangulation, ball2_barycenter, ellipse_retriangulation, ellipse_barycenter)): + descr = '\\frac{6}{\\pi\\delta^3}' + elif kType == GAUSSIAN: + if horizon < inf: + if dim == 1: + descr = '\\frac{4}{\\sqrt(\\pi) (\\operatorname{erf}(3)-6\\exp(-9)/\\sqrt(\\pi)) (\\delta/3)^3}' + elif dim == 2: + descr = '\\frac{4.0}{\\pi (1-10\\exp(-9)) (\\delta/3)^4}' else: - horizonFun = constant(sqrt(getREAL(self.params, fHORIZON2))) - scaling = variableFractionalLaplacianScalingWithDifferentHorizon(self.symmetric, self.normalized, self.boundary, self.derivative, horizonFun) - return scaling + descr = '\\frac{1}{(2\\pi\\sigma)^{d/2}}' + elif kType == EXPONENTIAL: + if horizon < inf: + descr = '\\frac{a^3}{2-exp(-a\\delta) (2+2a\\delta + (a\\delta)^2)}' + else: + descr = '\\frac{a^3}{2}' + elif kType == POLYNOMIAL: + descr = '' + return descr - def __repr__(self): - return 'variableFractionalLaplacianScaling' + + +cdef class constantIntegrableScaling(constantTwoPoint): + def __init__(self, kernelType kType, interactionDomain interaction, INDEX_t dim, REAL_t horizon, REAL_t gaussian_variance=1.0, REAL_t exponentialRate=1.0): + self.kType = kType + self.dim = dim + self.interaction = interaction + self.horizon = horizon + self.gaussian_variance = gaussian_variance + self.exponentialRate = exponentialRate + value = integrableScaling(kType, interaction, dim, horizon, gaussian_variance, exponentialRate) + super(constantIntegrableScaling, self).__init__(value) def __reduce__(self): - return variableFractionalLaplacianScaling, (self.symmetric, self.normalized, self.boundary, self.derivative) + return constantIntegrableScaling, (self.kType, self.interaction, self.dim, self.horizon, self.gaussian_variance, self.exponentialRate) + + def __repr__(self): + return '{}({} -> {})'.format(self.__class__.__name__, self.horizon, self.value) def getLongDescription(self): - cdef: - REAL_t horizon2 = getREAL(self.params, fHORIZON2) - if self.normalized: - if horizon2 < inf: - descr_C = '\\frac{(2-2s) horizon^{2s-2} d \\Gamma(d/2)}{\\pi^{d/2}}' - else: - descr_C = '\\frac{2^{2s} s \\Gamma(s+d/2)}{\\pi^{d/2} \\Gamma(1-s)}' - else: - descr_C = '' - if self.derivative == 0: - return descr_C - else: - descr = "MISSING" - return descr + return integrableScalingDescription(self.kType, self.interaction, self.dim, self.horizon, self.gaussian_variance, self.exponentialRate) cdef class variableIntegrableScaling(parametrizedTwoPointFunction): @@ -543,93 +459,12 @@ cdef class variableIntegrableScaling(parametrizedTwoPointFunction): parametrizedTwoPointFunction.setParams(self, params) self.dim = getINDEX(self.params, fKDIM) - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - cdef: - REAL_t horizon2 = getREAL(self.params, fHORIZON2) - - if horizon2 <= 0.: - value[0] = np.nan - else: - if self.kType == INDICATOR: - if self.dim == 1: - value[0] = 3./horizon2**1.5 / 2. - elif self.dim == 2: - if isinstance(self.interaction, (ball2_retriangulation, ball2_barycenter, ellipse_retriangulation, ellipse_barycenter)): - value[0] = 8./pi/horizon2**2 / 2. - elif isinstance(self.interaction, (ballInf_retriangulation, ballInf_barycenter)): - value[0] = 3./4./horizon2**2 / 2. - else: - raise NotImplementedError() - else: - raise NotImplementedError() - elif self.kType == PERIDYNAMIC: - if self.dim == 1: - value[0] = 2./horizon2 / 2. - elif self.dim == 2: - if isinstance(self.interaction, (ball2_retriangulation, ball2_barycenter, ellipse_retriangulation, ellipse_barycenter)): - value[0] = 6./pi/horizon2**1.5 / 2. - else: - raise NotImplementedError() - else: - raise NotImplementedError() - elif self.kType == GAUSSIAN: - if self.dim == 1: - # value[0] = 4.0/sqrt(pi)/(horizon/3.)**3 / 2. - value[0] = 4.0/sqrt(pi)/(erf(3.0)-6.0*exp(-9.0)/sqrt(pi))/(sqrt(horizon2)/3.0)**3 / 2. - elif self.dim == 2: - if isinstance(self.interaction, (ball2_retriangulation, ball2_barycenter, ellipse_retriangulation, ellipse_barycenter)): - # value[0] = 4.0/pi/(horizon/3.0)**4 / 2. - value[0] = 4.0/pi/(1.0-10.0*exp(-9.0))/(sqrt(horizon2)/3.0)**4 / 2. - else: - raise NotImplementedError() - else: - raise NotImplementedError() - else: - raise NotImplementedError() - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): cdef: REAL_t horizon2 = getREAL(self.params, fHORIZON2) - - if horizon2 <= 0.: - value[0] = np.nan - else: - if self.kType == INDICATOR: - if self.dim == 1: - value[0] = 3./horizon2**1.5 / 2. - elif self.dim == 2: - if isinstance(self.interaction, (ball2_retriangulation, ball2_barycenter, ellipse_retriangulation, ellipse_barycenter)): - value[0] = 8./pi/horizon2**2 / 2. - elif isinstance(self.interaction, (ballInf_retriangulation, ballInf_barycenter)): - value[0] = 3./4./horizon2**2 / 2. - else: - raise NotImplementedError() - else: - raise NotImplementedError() - elif self.kType == PERIDYNAMIC: - if self.dim == 1: - value[0] = 2./horizon2 / 2. - elif self.dim == 2: - if isinstance(self.interaction, (ball2_retriangulation, ball2_barycenter, ellipse_retriangulation, ellipse_barycenter)): - value[0] = 6./pi/horizon2**1.5 / 2. - else: - raise NotImplementedError() - else: - raise NotImplementedError() - elif self.kType == GAUSSIAN: - if self.dim == 1: - # value[0] = 4.0/sqrt(pi)/(horizon/3.)**3 / 2. - value[0] = 4.0/sqrt(pi)/(erf(3.0)-6.0*exp(-9.0)/sqrt(pi))/(sqrt(horizon2)/3.0)**3 / 2. - elif self.dim == 2: - if isinstance(self.interaction, (ball2_retriangulation, ball2_barycenter, ellipse_retriangulation, ellipse_barycenter)): - # value[0] = 4.0/pi/(horizon/3.0)**4 / 2. - value[0] = 4.0/pi/(1.0-10.0*exp(-9.0))/(sqrt(horizon2)/3.0)**4 / 2. - else: - raise NotImplementedError() - else: - raise NotImplementedError() - else: - raise NotImplementedError() + REAL_t gaussian_variance = -1.0 + REAL_t exponentialRate = -1.0 + value[0] = integrableScaling(self.kType, self.interaction, self.dim, sqrt(horizon2), gaussian_variance, exponentialRate) def getScalingWithDifferentHorizon(self): cdef: @@ -649,46 +484,12 @@ cdef class variableIntegrableScaling(parametrizedTwoPointFunction): def __reduce__(self): return variableIntegrableScaling, (self.kType, self.interaction) - -###################################################################### - - -cdef class variableFractionalLaplacianScalingWithDifferentHorizon(variableFractionalLaplacianScaling): - def __init__(self, BOOL_t symmetric, BOOL_t normalized, BOOL_t boundary, INDEX_t derivative, function horizonFun): - super(variableFractionalLaplacianScalingWithDifferentHorizon, self).__init__(symmetric, normalized, boundary, derivative) - self.horizonFun = horizonFun - - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - cdef: - void* params - void* paramsModified = PyMem_Malloc(NUM_KERNEL_PARAMS*OFFSET) - REAL_t horizon, scalingValue - horizon = self.horizonFun.eval(x) - params = self.getParams() - memcpy(paramsModified, params, NUM_KERNEL_PARAMS*OFFSET) - setREAL(paramsModified, fHORIZON2, horizon**2) - self.setParams(paramsModified) - variableFractionalLaplacianScaling.evalPtr(self, x.shape[0], &x[0], &y[0], &scalingValue) - self.setParams(params) - value[0] = scalingValue - - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): + def getLongDescription(self): cdef: - void* params - void* paramsModified = PyMem_Malloc(NUM_KERNEL_PARAMS*OFFSET) - REAL_t horizon, scalingValue - REAL_t[::1] xA = x - horizon = self.horizonFun.eval(xA) - params = self.getParams() - memcpy(paramsModified, params, NUM_KERNEL_PARAMS*OFFSET) - setREAL(paramsModified, fHORIZON2, horizon**2) - self.setParams(paramsModified) - variableFractionalLaplacianScaling.evalPtr(self, dim, x, y, &scalingValue) - self.setParams(params) - value[0] = scalingValue - - def __reduce__(self): - return variableFractionalLaplacianScalingWithDifferentHorizon, (self.symmetric, self.normalized, self.boundary, self.derivative, self.horizonFun) + REAL_t horizon2 = getREAL(self.params, fHORIZON2) + REAL_t gaussian_variance = -1.0 + REAL_t exponentialRate = -1.0 + return integrableScalingDescription(self.kType, self.interaction, self.dim, sqrt(horizon2), gaussian_variance, exponentialRate) cdef class variableIntegrableScalingWithDifferentHorizon(variableIntegrableScaling): @@ -696,20 +497,6 @@ cdef class variableIntegrableScalingWithDifferentHorizon(variableIntegrableScali super(variableIntegrableScalingWithDifferentHorizon, self).__init__(kType, interaction) self.horizonFun = horizonFun - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - cdef: - void* params - void* paramsModified = PyMem_Malloc(NUM_KERNEL_PARAMS*OFFSET) - REAL_t horizon, scalingValue - horizon = self.horizonFun.eval(x) - params = self.getParams() - memcpy(paramsModified, params, NUM_KERNEL_PARAMS*OFFSET) - setREAL(paramsModified, fHORIZON2, horizon**2) - self.setParams(paramsModified) - variableIntegrableScaling.evalPtr(self, x.shape[0], &x[0], &y[0], &scalingValue) - self.setParams(params) - value[0] = scalingValue - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): cdef: void* params diff --git a/nl/PyNucleus_nl/kernel_params.pxi b/nl/PyNucleus_nl/kernel_params.pxi index 0e69f8e3..10de5cdc 100644 --- a/nl/PyNucleus_nl/kernel_params.pxi +++ b/nl/PyNucleus_nl/kernel_params.pxi @@ -9,7 +9,7 @@ cdef enum: OFFSET = sizeof(void*) cdef enum: - NUM_KERNEL_PARAMS = 12 + NUM_KERNEL_PARAMS = 15 cdef enum kernelParams: fS = 0*OFFSET @@ -23,64 +23,67 @@ cdef enum kernelParams: fEVAL = 8*OFFSET fINTERACTION = 9*OFFSET fEXPONENTINVERSE = 10*OFFSET - fTEMPERED=10*OFFSET - fGREENS_LAMBDA=10*OFFSET + fEXPONENT = 13*OFFSET + fTEMPERED = 10*OFFSET + fGREENS_LAMBDA = 10*OFFSET fBOUNDARY=11*OFFSET + fMANIFOLD=12*OFFSET + fLOG_SINGULARITY = 14*OFFSET -cdef inline BOOL_t isNull(void *c_params, size_t pos): +cdef inline BOOL_t isNull(void *c_params, size_t pos) noexcept: return (((c_params)+pos))[0] == NULL -cdef inline BOOL_t getBOOL(void *c_params, size_t pos): +cdef inline BOOL_t getBOOL(void *c_params, size_t pos) noexcept: return (((c_params)+pos))[0] -cdef inline void setBOOL(void *c_params, size_t pos, BOOL_t val): +cdef inline void setBOOL(void *c_params, size_t pos, BOOL_t val) noexcept: (((c_params)+pos))[0] = val -cdef inline INDEX_t getINDEX(void *c_params, size_t pos): +cdef inline INDEX_t getINDEX(void *c_params, size_t pos) noexcept: return (((c_params)+pos))[0] -cdef inline void setINDEX(void *c_params, size_t pos, INDEX_t val): +cdef inline void setINDEX(void *c_params, size_t pos, INDEX_t val) noexcept: (((c_params)+pos))[0] = val -cdef inline REAL_t getREAL(void *c_params, size_t pos): +cdef inline REAL_t getREAL(void *c_params, size_t pos) noexcept: return (((c_params)+pos))[0] -cdef inline void setREAL(void *c_params, size_t pos, REAL_t val): +cdef inline void setREAL(void *c_params, size_t pos, REAL_t val) noexcept: (((c_params)+pos))[0] = val -cdef inline COMPLEX_t getCOMPLEX(void *c_params, size_t pos): +cdef inline COMPLEX_t getCOMPLEX(void *c_params, size_t pos) noexcept: return (((c_params)+pos))[0] -cdef inline void setCOMPLEX(void *c_params, size_t pos, COMPLEX_t val): +cdef inline void setCOMPLEX(void *c_params, size_t pos, COMPLEX_t val) noexcept: (((c_params)+pos))[0] = val ctypedef REAL_t (*fun_t)(REAL_t *x, REAL_t *y, void *c_params) -cdef inline void setFun(void *c_params, size_t pos, fun_t val): +cdef inline void setFun(void *c_params, size_t pos, fun_t val) noexcept: (((c_params)+pos))[0] = val -cdef inline fun_t getFun(void *c_params, size_t pos): +cdef inline fun_t getFun(void *c_params, size_t pos) noexcept: return (((c_params)+pos))[0] ctypedef COMPLEX_t (*complex_fun_t)(REAL_t *x, REAL_t *y, void *c_params) -cdef inline void setComplexFun(void *c_params, size_t pos, complex_fun_t val): +cdef inline void setComplexFun(void *c_params, size_t pos, complex_fun_t val) noexcept: (((c_params)+pos))[0] = val -cdef inline complex_fun_t getComplexFun(void *c_params, size_t pos): +cdef inline complex_fun_t getComplexFun(void *c_params, size_t pos) noexcept: return (((c_params)+pos))[0] -cdef inline REAL_t* getREALArray1D(void *c_params, size_t pos): +cdef inline REAL_t* getREALArray1D(void *c_params, size_t pos) noexcept: return (((c_params)+pos))[0] -cdef inline void setREALArray1D(void *c_params, size_t pos, REAL_t[::1] val): +cdef inline void setREALArray1D(void *c_params, size_t pos, REAL_t[::1] val) noexcept: (((c_params)+pos))[0] = &val[0] -cdef inline REAL_t* getREALArray2D(void *c_params, size_t pos): +cdef inline REAL_t* getREALArray2D(void *c_params, size_t pos) noexcept: return (((c_params)+pos))[0] -cdef inline void setREALArray2D(void *c_params, size_t pos, REAL_t[:, ::1] val): +cdef inline void setREALArray2D(void *c_params, size_t pos, REAL_t[:, ::1] val) noexcept: (((c_params)+pos))[0] = &val[0, 0] @@ -96,3 +99,4 @@ cpdef enum: EXPONENTIAL = 8 POLYNOMIAL = 9 MANIFOLD_FRACTIONAL = 10 + ERROR = 11 diff --git a/nl/PyNucleus_nl/kernels.pxd b/nl/PyNucleus_nl/kernels.pxd new file mode 100644 index 00000000..03472323 --- /dev/null +++ b/nl/PyNucleus_nl/kernels.pxd @@ -0,0 +1,39 @@ +################################################################################### +# Copyright 2021 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. # +# If you want to use this code, please refer to the README.rst and LICENSE files. # +################################################################################### + +from PyNucleus_base.myTypes cimport INDEX_t, REAL_t, COMPLEX_t, BOOL_t +from PyNucleus_fem.functions cimport function +from . twoPointFunctions cimport twoPointFunction, ComplextwoPointFunction, constantTwoPoint, parametrizedTwoPointFunction +from . interactionDomains cimport interactionDomain +from . fractionalOrders cimport fractionalOrderBase, singleVariableUnsymmetricFractionalOrder + +include "kernel_params_decl.pxi" +include "kernels_decl_REAL.pxi" +include "kernels_decl_COMPLEX.pxi" +include "fractionalKernel_decl.pxi" + + +cdef class RangedFractionalKernel(FractionalKernel): + cdef: + public admissibleOrders + public BOOL_t normalized + public REAL_t errorBound + public INDEX_t M_min + public INDEX_t M_max + public REAL_t xi + public REAL_t tempered + + +cdef class RangedVariableFractionalKernel(FractionalKernel): + cdef: + public function blockIndicator + public admissibleOrders + public BOOL_t normalized + public REAL_t errorBound + public INDEX_t M_min + public INDEX_t M_max + public REAL_t xi diff --git a/nl/PyNucleus_nl/kernels.py b/nl/PyNucleus_nl/kernels.py deleted file mode 100644 index f286411b..00000000 --- a/nl/PyNucleus_nl/kernels.py +++ /dev/null @@ -1,233 +0,0 @@ -################################################################################### -# Copyright 2021 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. # -# If you want to use this code, please refer to the README.rst and LICENSE files. # -################################################################################### - -import numpy as np -from PyNucleus_base.myTypes import INDEX, REAL -from PyNucleus_fem.functions import function, constant -from PyNucleus_fem.mesh import meshNd -from . twoPointFunctions import constantTwoPoint, inverseTwoPoint -from . interactionDomains import (interactionDomain, - fullSpace, - ball1_retriangulation, - ball2_retriangulation, - ballInf_retriangulation) -from . fractionalOrders import (fractionalOrderBase, - constFractionalOrder, - variableConstFractionalOrder, - singleVariableUnsymmetricFractionalOrder) -from . kernelNormalization import (constantFractionalLaplacianScaling, - constantFractionalLaplacianScalingDerivative, - variableFractionalLaplacianScaling, - constantIntegrableScaling, - variableIntegrableScaling, - ) -from . kernelsCy import (Kernel, - FractionalKernel, - RangedFractionalKernel, - FRACTIONAL, - LOGINVERSEDISTANCE, - GREENS_2D, - GREENS_3D, - MONOMIAL, - getKernelEnum) -from . operatorInterpolation import admissibleSet -import warnings - - -def _getDim(dim): - if isinstance(dim, meshNd): - return dim.dim - elif isinstance(dim, (INDEX, int)): - return dim - else: - raise NotImplementedError('Dim: {}'.format(dim)) - - -def _getKernelType(kernel): - if isinstance(kernel, str): - kType = getKernelEnum(kernel) - elif isinstance(kernel, int): - kType = kernel - else: - raise NotImplementedError('Kernel type: {}'.format(kernel)) - return kType - - -def _getFractionalOrder(s): - if isinstance(s, fractionalOrderBase): - sFun = s - elif isinstance(s, admissibleSet): - sFun = s - elif isinstance(s, tuple) and len(s) == 2: - sFun = admissibleSet(s) - elif isinstance(s, (REAL, float)): - sFun = constFractionalOrder(s) - else: - raise NotImplementedError('Fractional order: {}'.format(s)) - return sFun - - -def _getHorizon(horizon): - if isinstance(horizon, function): - horizonFun = horizon - elif isinstance(horizon, (REAL, float, int)): - horizonFun = constant(horizon) - elif horizon is None: - horizonFun = constant(np.inf) - else: - raise NotImplementedError('Horizon: {}'.format(horizon)) - return horizonFun - - -def _getInteraction(interaction, horizon): - if isinstance(interaction, interactionDomain): - pass - elif isinstance(horizon, constant) and horizon.value == np.inf: - interaction = fullSpace() - elif interaction is None: - interaction = ball2_retriangulation(horizon) - elif isinstance(interaction, str): - if interaction == 'fullSpace': - interaction = fullSpace() - elif interaction == 'ball1': - interaction = ball1_retriangulation(horizon) - elif interaction == 'ball2': - interaction = ball2_retriangulation(horizon) - elif interaction == 'ballInf': - interaction = ballInf_retriangulation(horizon) - else: - raise NotImplementedError('Interaction: {}'.format(interaction)) - else: - raise NotImplementedError('Interaction: {}'.format(interaction)) - return interaction - - -def getFractionalKernel(dim, - s, - horizon=None, - interaction=None, - scaling=None, - normalized=True, - piecewise=True, - phi=None, - boundary=False, - derivative=0, - tempered=0., - max_horizon=np.nan, - manifold=False): - dim_ = _getDim(dim) - sFun = _getFractionalOrder(s) - horizonFun = _getHorizon(horizon) - interaction = _getInteraction(interaction, horizonFun) - - if isinstance(sFun, admissibleSet): - kernel = RangedFractionalKernel(dim_, sFun, horizonFun, normalized=normalized, tempered=tempered) - else: - if scaling is None: - if isinstance(sFun, constFractionalOrder) and isinstance(horizonFun, constant): - if derivative == 0: - if normalized: - if not manifold: - scaling = constantFractionalLaplacianScaling(dim, sFun.value, horizonFun.value, tempered) - else: - scaling = constantFractionalLaplacianScalingManifold(dim-1, sFun.value, horizonFun.value, tempered) - else: - scaling = constantTwoPoint(0.5) - else: - if piecewise: - warnings.warn('Derivative kernels cannot be piecewise. Switching to piecewise == False.') - piecewise = False - scaling = constantFractionalLaplacianScalingDerivative(dim, sFun.value, horizonFun.value, normalized, boundary, derivative, tempered) - else: - symmetric = sFun.symmetric and isinstance(horizonFun, constant) - if piecewise and isinstance(sFun, singleVariableUnsymmetricFractionalOrder): - warnings.warn('Variable s kernels cannot be piecewise. Switching to piecewise == False.') - piecewise = False - scaling = variableFractionalLaplacianScaling(symmetric, normalized, boundary, derivative) - if boundary: - if isinstance(sFun, (constFractionalOrder, - variableConstFractionalOrder)): - fac = constantTwoPoint(1/sFun.value) - else: - fac = inverseTwoPoint(sFun) - if phi is not None: - phi = fac*phi - else: - phi = fac - kernel = FractionalKernel(dim_, sFun, horizonFun, interaction, scaling, phi, piecewise=piecewise, boundary=boundary, - derivative=derivative, tempered=tempered, max_horizon=max_horizon, manifold=manifold) - - from . twoPointFunctions import parametrizedTwoPointFunction - if isinstance(kernel.scaling, parametrizedTwoPointFunction): - assert kernel.getParamPtrAddr() == kernel.scaling.getParamPtrAddr() - if isinstance(kernel.interaction, parametrizedTwoPointFunction): - assert kernel.getParamPtrAddr() == kernel.interaction.getParamPtrAddr() - return kernel - - -def getIntegrableKernel(dim, - kernel, - horizon, - scaling=None, - interaction=None, - normalized=True, - piecewise=True, - phi=None, - boundary=False, - monomialPower=np.nan, - variance=1., - exponentialRate=1.0, - a=1., - max_horizon=np.nan): - dim_ = _getDim(dim) - kType = _getKernelType(kernel) - horizonFun = _getHorizon(horizon) - interaction = _getInteraction(interaction, horizonFun) - - if scaling is None: - if normalized: - if isinstance(horizonFun, constant): - scaling = constantIntegrableScaling(kType, interaction, dim_, horizonFun.value, gaussian_variance=variance, exponentialRate=exponentialRate) - else: - scaling = variableIntegrableScaling(kType, interaction) - else: - scaling = constantTwoPoint(0.5) - if (not scaling.symmetric) or (phi is not None and not phi.symmetric): - piecewise = False - return Kernel(dim_, kType=kType, horizon=horizonFun, interaction=interaction, scaling=scaling, phi=phi, piecewise=piecewise, - boundary=boundary, monomialPower=monomialPower, max_horizon=max_horizon, variance=variance, exponentialRate=exponentialRate, a=a) - - -def getKernel(dim, - s=None, - horizon=None, - scaling=None, - interaction=None, - normalized=True, - piecewise=True, - phi=None, - kernel=FRACTIONAL, - boundary=False, - max_horizon=np.nan, - variance=1., - exponentialRate=1.0): - kType = _getKernelType(kernel) - if kType == FRACTIONAL: - return getFractionalKernel(dim, s, horizon, interaction, scaling, normalized, piecewise, phi, boundary, max_horizon=max_horizon) - else: - return getIntegrableKernel(dim, - kernel=kType, - horizon=horizon, - scaling=scaling, - interaction=interaction, - normalized=normalized, - piecewise=piecewise, phi=phi, - max_horizon=max_horizon, - variance=variance, - exponentialRate=exponentialRate) - - diff --git a/nl/PyNucleus_nl/kernels.pyx b/nl/PyNucleus_nl/kernels.pyx new file mode 100644 index 00000000..5bb3245a --- /dev/null +++ b/nl/PyNucleus_nl/kernels.pyx @@ -0,0 +1,325 @@ +################################################################################### +# Copyright 2021 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. # +# If you want to use this code, please refer to the README.rst and LICENSE files. # +################################################################################### + +from cpython.mem cimport PyMem_Malloc, PyMem_Free +from libc.math cimport (sin, cos, sqrt, + log, + pow, + exp, + M_PI as pi) +from scipy.special.cython_special cimport gammaincc, gamma, hankel1 +from scipy.special import eval_legendre +import numpy as np +cimport numpy as np +from PyNucleus_base.myTypes import REAL, COMPLEX, INDEX +from PyNucleus_base import uninitialized +from PyNucleus_base.blas cimport mydot, assign +from PyNucleus_fem.functions cimport constant +from PyNucleus_fem.lookupFunction import (UniformLookup1D, + Lookup1D) +from . interactionDomains cimport (interactionDomain, + fullSpace, + functionOfDistance, + ball1_retriangulation, + ball2_retriangulation, + ballInf_retriangulation) +from . twoPointFunctions cimport (constantTwoPoint, + productTwoPoint, + inverseTwoPoint, + productParametrizedTwoPoint) +from . fractionalOrders cimport (constFractionalOrder, + fractionalOrderBase, + variableFractionalOrder, + variableConstFractionalOrder, + singleVariableUnsymmetricFractionalOrder, + piecewiseConstantFractionalOrder) +from . kernelNormalization cimport (constantFractionalLaplacianScaling, + constantIntegrableScaling, + variableFractionalLaplacianScaling, + variableFractionalLaplacianScalingWithDifferentHorizon, + variableIntegrableScaling, + variableIntegrableScalingWithDifferentHorizon) +from . operatorInterpolation import admissibleSet +from copy import deepcopy + + +cdef inline REAL_t gammainc(REAL_t a, REAL_t x) noexcept: + return gamma(a)*gammaincc(a, x) + + +cdef inline COMPLEX_t hankel10complex(REAL_t x) noexcept: + return 1j*hankel1(0., x) + + +include "kernel_params.pxi" + + +def _getKernelType(kernel): + if isinstance(kernel, str): + kType = getKernelEnum(kernel) + elif isinstance(kernel, int): + kType = kernel + else: + raise NotImplementedError('Kernel type: {}'.format(kernel)) + return kType + + +cpdef INDEX_t _getDim(dim): + from PyNucleus_fem.mesh import meshNd + if isinstance(dim, meshNd): + return dim.dim + elif isinstance(dim, (INDEX, int)): + return dim + else: + raise NotImplementedError('Dim: {}'.format(dim)) + + +def _getFractionalOrder(s): + if isinstance(s, fractionalOrderBase): + sFun = s + elif isinstance(s, admissibleSet): + sFun = s + elif isinstance(s, tuple) and len(s) == 2: + sFun = admissibleSet(s) + elif isinstance(s, (REAL, float)): + sFun = constFractionalOrder(s) + else: + raise NotImplementedError('Fractional order: {}'.format(s)) + return sFun + + +cpdef function _getHorizon(horizon): + if isinstance(horizon, function): + horizonFun = horizon + elif isinstance(horizon, (REAL, float, int)): + horizonFun = constant(horizon) + elif horizon is None: + horizonFun = constant(np.inf) + else: + raise NotImplementedError('Horizon: {}'.format(horizon)) + return horizonFun + + +cpdef interactionDomain _getInteraction(interaction, horizon): + if isinstance(interaction, interactionDomain): + pass + elif isinstance(horizon, constant) and horizon.value == np.inf: + interaction = fullSpace() + elif interaction is None: + interaction = ball2_retriangulation(horizon) + elif isinstance(interaction, str): + if interaction == 'fullSpace': + interaction = fullSpace() + elif interaction == 'ball1': + interaction = ball1_retriangulation(horizon) + elif interaction == 'ball2': + interaction = ball2_retriangulation(horizon) + elif interaction == 'ballInf': + interaction = ballInf_retriangulation(horizon) + else: + raise NotImplementedError('Interaction: {}'.format(interaction)) + else: + raise NotImplementedError('Interaction: {}'.format(interaction)) + return interaction + + +def getKernelEnum(str kernelTypeString): + if kernelTypeString.upper() == "FRACTIONAL": + return FRACTIONAL + elif kernelTypeString.upper() in ("INDICATOR", "CONSTANT"): + return INDICATOR + elif kernelTypeString.upper() in ("INVERSEDISTANCE", "INVERSEOFDISTANCE", "PERIDYNAMIC"): + return PERIDYNAMIC + elif kernelTypeString.upper() == "GAUSSIAN": + return GAUSSIAN + elif kernelTypeString.upper() == "EXPONENTIAL": + return EXPONENTIAL + elif kernelTypeString.upper() == "POLYNOMIAL": + return POLYNOMIAL + elif kernelTypeString.upper() == "LOGINVERSEDISTANCE": + return LOGINVERSEDISTANCE + elif kernelTypeString.upper() == "MONOMIAL": + return MONOMIAL + elif kernelTypeString.upper() == "GREENS_2D": + return GREENS_2D + elif kernelTypeString.upper() == "GREENS_3D": + return GREENS_3D + elif kernelTypeString.upper() == "MANIFOLD_FRACTIONAL": + return MANIFOLD_FRACTIONAL + else: + raise NotImplementedError(kernelTypeString) + + +cdef REAL_t monomialKernel(REAL_t *x, REAL_t *y, void *c_params): + cdef: + INDEX_t dim = getINDEX(c_params, fKDIM) + interactionDomain interaction = (((c_params+fINTERACTION))[0]) + REAL_t C, inter, d2, singularityValue + interaction.evalPtr(dim, x, y, &inter) + if inter != 0.: + d2 = interaction.dist2 + C = getREAL(c_params, fSCALING) + singularityValue = getREAL(c_params, fEXPONENT) + return C*pow(d2, 0.5*singularityValue) + else: + return 0. + + +cdef REAL_t monomialLogKernel(REAL_t *x, REAL_t *y, void *c_params): + cdef: + INDEX_t dim = getINDEX(c_params, fKDIM) + interactionDomain interaction = (((c_params+fINTERACTION))[0]) + REAL_t C, inter, d2, singularityValue, logPower + interaction.evalPtr(dim, x, y, &inter) + if inter != 0.: + d2 = interaction.dist2 + C = getREAL(c_params, fSCALING) + singularityValue = getREAL(c_params, fEXPONENT) + logPower = getREAL(c_params, fLOG_SINGULARITY) + return C*pow(d2, 0.5*singularityValue)*pow(-0.5*log(d2), logPower) + else: + return 0. + + +cdef REAL_t temperedMonomialKernel(REAL_t *x, REAL_t *y, void *c_params): + cdef: + REAL_t singularityValue, C, lam, d2, inter + INDEX_t dim = getINDEX(c_params, fKDIM) + interactionDomain interaction = (((c_params+fINTERACTION))[0]) + interaction.evalPtr(dim, x, y, &inter) + if inter != 0.: + d2 = interaction.dist2 + singularityValue = getREAL(c_params, fEXPONENT) + C = getREAL(c_params, fSCALING) + lam = getREAL(c_params, fTEMPERED) + return C*pow(d2, 0.5*singularityValue)*exp(-lam*sqrt(d2)) + else: + return 0. + + +cdef REAL_t temperedMonomialKernelBoundary(REAL_t *x, REAL_t *y, void *c_params): + cdef: + REAL_t s, C, lam, d2, inter + INDEX_t dim = getINDEX(c_params, fKDIM) + interactionDomain interaction = (((c_params+fINTERACTION))[0]) + interaction.evalPtr(dim, x, y, &inter) + if inter != 0.: + s = getREAL(c_params, fS) + C = getREAL(c_params, fSCALING) + lam = getREAL(c_params, fTEMPERED) + d2 = interaction.dist2 + return C*pow(d2, 0.5-0.5*dim-s)*gammainc(-2*s, lam*sqrt(d2)) + else: + return 0. + + +cdef REAL_t gaussianKernel(REAL_t *x, REAL_t *y, void *c_params): + cdef: + INDEX_t dim = getINDEX(c_params, fKDIM) + interactionDomain interaction = (((c_params+fINTERACTION))[0]) + REAL_t C, invD + REAL_t d2, inter + interaction.evalPtr(dim, x, y, &inter) + if inter != 0.: + d2 = interaction.dist2 + C = getREAL(c_params, fSCALING) + invD = getREAL(c_params, fEXPONENTINVERSE) + return C*exp(-d2*invD) + else: + return 0. + + +cdef REAL_t gaussianKernelBoundary(REAL_t *x, REAL_t *y, void *c_params): + cdef: + INDEX_t dim = getINDEX(c_params, fKDIM) + interactionDomain interaction = (((c_params+fINTERACTION))[0]) + REAL_t C, invD + REAL_t d2, inter + interaction.evalPtr(dim, x, y, &inter) + if inter != 0.: + d2 = interaction.dist2 + C = getREAL(c_params, fSCALING) + invD = getREAL(c_params, fEXPONENTINVERSE) + return -0.5*C*pow(d2*invD, -0.5*dim)*gammainc(0.5*dim, d2*invD)*sqrt(d2) + else: + return 0. + + +cdef REAL_t polynomialKernel(REAL_t *x, REAL_t *y, void *c_params): + cdef: + INDEX_t dim = getINDEX(c_params, fKDIM) + interactionDomain interaction = (((c_params+fINTERACTION))[0]) + REAL_t C, a + REAL_t d2, inter + interaction.evalPtr(dim, x, y, &inter) + if inter != 0.: + d2 = interaction.dist2 + C = getREAL(c_params, fSCALING) + a = getREAL(c_params, fEXPONENTINVERSE) + return C*(a**3*d2)/(a**2+d2)**2 + else: + return 0. + + +cdef REAL_t polynomialKernel1Dboundary(REAL_t *x, REAL_t *y, void *c_params): + cdef: + interactionDomain interaction = (((c_params+fINTERACTION))[0]) + REAL_t C, a + REAL_t d2, inter + interaction.evalPtr(1, x, y, &inter) + if inter != 0.: + d2 = interaction.dist2 + C = getREAL(c_params, fSCALING) + a = getREAL(c_params, fEXPONENTINVERSE) + return -0.5*C*(-a**2/(2*sqrt(d2)) + a**3/2/(a**2+d2)) + else: + return 0. + + +cdef REAL_t logInverseDistanceKernel(REAL_t *x, REAL_t *y, void *c_params): + cdef: + INDEX_t dim = getINDEX(c_params, fKDIM) + interactionDomain interaction = (((c_params+fINTERACTION))[0]) + REAL_t C + REAL_t d2, inter + interaction.evalPtr(dim, x, y, &inter) + if inter != 0.: + d2 = interaction.dist2 + C = getREAL(c_params, fSCALING) + return -0.5*C*log(d2) + else: + return 0. + + +cdef COMPLEX_t greens2Dcomplex(REAL_t *x, REAL_t *y, void *c_params): + cdef: + INDEX_t dim = getINDEX(c_params, fKDIM) + interactionDomain interaction = (((c_params+fINTERACTION))[0]) + REAL_t C = getREAL(c_params, fSCALING) + REAL_t lam = getREAL(c_params, fGREENS_LAMBDA) + REAL_t inter + interaction.evalPtr(dim, x, y, &inter) + C = getREAL(c_params, fSCALING) + return C*hankel10complex(lam*sqrt(interaction.d2)) + + +cdef COMPLEX_t greens3Dcomplex(REAL_t *x, REAL_t *y, void *c_params): + cdef: + INDEX_t dim = getINDEX(c_params, fKDIM) + interactionDomain interaction = (((c_params+fINTERACTION))[0]) + REAL_t C = getREAL(c_params, fSCALING) + COMPLEX_t lam = getCOMPLEX(c_params, fGREENS_LAMBDA) + REAL_t d, inter + interaction.evalPtr(dim, x, y, &inter) + C = getREAL(c_params, fSCALING) + d = sqrt(interaction.d2) + return C*exp(-lam.real*d)*(cos(-lam.imag*d)+1j*sin(-lam.imag*d))/d + + +include "kernels_REAL.pxi" +include "kernels_COMPLEX.pxi" +include "fractionalKernel.pxi" diff --git a/nl/PyNucleus_nl/kernelsCy.pxd b/nl/PyNucleus_nl/kernelsCy.pxd deleted file mode 100644 index 4798a45e..00000000 --- a/nl/PyNucleus_nl/kernelsCy.pxd +++ /dev/null @@ -1,128 +0,0 @@ -################################################################################### -# Copyright 2021 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. # -# If you want to use this code, please refer to the README.rst and LICENSE files. # -################################################################################### - -from PyNucleus_base.myTypes cimport INDEX_t, REAL_t, COMPLEX_t, BOOL_t -from PyNucleus_fem.functions cimport function -from . twoPointFunctions cimport twoPointFunction, ComplextwoPointFunction, constantTwoPoint, parametrizedTwoPointFunction -from . interactionDomains cimport interactionDomain -from . fractionalOrders cimport fractionalOrderBase - -include "kernel_params_decl.pxi" - - -ctypedef REAL_t (*kernel_fun_t)(REAL_t *x, REAL_t *y, void* user_data) -ctypedef COMPLEX_t (*complex_kernel_fun_t)(REAL_t *x, REAL_t *y, void* user_data) - - -cdef class Kernel(twoPointFunction): - cdef: - public INDEX_t dim - public kernelType kernelType - public REAL_t min_singularity - public REAL_t max_singularity - public REAL_t max_horizon - public function horizon - public interactionDomain interaction - public twoPointFunction scalingPrePhi - public twoPointFunction scaling - public twoPointFunction phi - public BOOL_t variableSingularity - public BOOL_t variableHorizon - public BOOL_t finiteHorizon - public BOOL_t complement - public BOOL_t variableScaling - public BOOL_t variable - public BOOL_t piecewise - kernel_fun_t kernelFun - void *c_kernel_params - cdef BOOL_t getBoundary(self) - cdef void setBoundary(self, BOOL_t boundary) - cdef REAL_t getSingularityValue(self) - cdef void setSingularityValue(self, REAL_t singularity) - cdef REAL_t getHorizonValue(self) - cdef void setHorizonValue(self, REAL_t horizon) - cdef REAL_t getHorizonValue2(self) - cdef REAL_t getScalingValue(self) - cdef void setScalingValue(self, REAL_t scaling) - cdef void evalParamsOnSimplices(self, REAL_t[::1] center1, REAL_t[::1] center2, REAL_t[:, ::1] simplex1, REAL_t[:, ::1] simplex2) - cdef void evalParams(self, REAL_t[::1] x, REAL_t[::1] y) - cdef void evalParamsPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y) - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value) - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value) - - -cdef class ComplexKernel(ComplextwoPointFunction): - cdef: - public INDEX_t dim - public kernelType kernelType - public REAL_t min_singularity - public REAL_t max_singularity - public REAL_t max_horizon - public function horizon - public interactionDomain interaction - public twoPointFunction scalingPrePhi - public twoPointFunction scaling - public twoPointFunction phi - public BOOL_t variableSingularity - public BOOL_t variableHorizon - public BOOL_t finiteHorizon - public BOOL_t complement - public BOOL_t variableScaling - public BOOL_t variable - public BOOL_t piecewise - public BOOL_t boundary - complex_kernel_fun_t kernelFun - void *c_kernel_params - cdef REAL_t getSingularityValue(self) - cdef void setSingularityValue(self, REAL_t singularity) - cdef REAL_t getHorizonValue(self) - cdef void setHorizonValue(self, REAL_t horizon) - cdef REAL_t getHorizonValue2(self) - cdef REAL_t getScalingValue(self) - cdef void setScalingValue(self, REAL_t scaling) - cdef void evalParamsOnSimplices(self, REAL_t[::1] center1, REAL_t[::1] center2, REAL_t[:, ::1] simplex1, REAL_t[:, ::1] simplex2) - cdef void evalParams(self, REAL_t[::1] x, REAL_t[::1] y) - cdef void evalParamsPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y) - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, COMPLEX_t[::1] value) - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, COMPLEX_t* value) - - -cdef class FractionalKernel(Kernel): - cdef: - public fractionalOrderBase s - public BOOL_t variableOrder - public INDEX_t derivative - public BOOL_t manifold - REAL_t[::1] tempVec - cdef REAL_t getsValue(self) - cdef void setsValue(self, REAL_t s) - cdef REAL_t gettemperedValue(self) - cdef void settemperedValue(self, REAL_t tempered) - - -cdef class RangedFractionalKernel(FractionalKernel): - cdef: - public admissibleOrders - public BOOL_t normalized - public REAL_t errorBound - public INDEX_t M_min - public INDEX_t M_max - public REAL_t xi - public REAL_t tempered - - -cdef class RangedVariableFractionalKernel(FractionalKernel): - cdef: - public function blockIndicator - public admissibleOrders - public BOOL_t normalized - public REAL_t errorBound - public INDEX_t M_min - public INDEX_t M_max - public REAL_t xi - - diff --git a/nl/PyNucleus_nl/kernelsCy.pyx b/nl/PyNucleus_nl/kernelsCy.pyx deleted file mode 100644 index e13bbe98..00000000 --- a/nl/PyNucleus_nl/kernelsCy.pyx +++ /dev/null @@ -1,2214 +0,0 @@ -################################################################################### -# Copyright 2021 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. # -# If you want to use this code, please refer to the README.rst and LICENSE files. # -################################################################################### - -from cpython.mem cimport PyMem_Malloc, PyMem_Free -from libc.math cimport (sin, cos, sqrt, - acos, - log, - fabs as abs, pow, - exp, - M_PI as pi) -from scipy.special.cython_special cimport gammaincc, gamma, hankel1 -import numpy as np -cimport numpy as np -from PyNucleus_base.myTypes import REAL -from PyNucleus_base import uninitialized -from PyNucleus_fem.functions cimport constant -from . interactionDomains cimport ball2_retriangulation, fullSpace -from . twoPointFunctions cimport (constantTwoPoint, - productTwoPoint, - inverseTwoPoint, - productParametrizedTwoPoint) -from . fractionalOrders cimport (constFractionalOrder, - variableFractionalOrder, - piecewiseConstantFractionalOrder) -from . kernelNormalization cimport (constantFractionalLaplacianScaling, - constantFractionalLaplacianScalingDerivative, - variableFractionalLaplacianScaling, - variableFractionalLaplacianScalingBoundary, - variableFractionalLaplacianScalingWithDifferentHorizon, - variableIntegrableScaling, - variableIntegrableScalingWithDifferentHorizon) -from copy import deepcopy - - -cdef inline REAL_t gammainc(REAL_t a, REAL_t x): - return gamma(a)*gammaincc(a, x) - - -cdef inline COMPLEX_t hankel10complex(REAL_t x): - return 1j*hankel1(0., x) - - -include "kernel_params.pxi" - - -def getKernelEnum(str kernelTypeString): - if kernelTypeString.upper() == "FRACTIONAL": - return FRACTIONAL - elif kernelTypeString.upper() in ("INDICATOR", "CONSTANT"): - return INDICATOR - elif kernelTypeString.upper() in ("INVERSEDISTANCE", "INVERSEOFDISTANCE", "PERIDYNAMIC"): - return PERIDYNAMIC - elif kernelTypeString.upper() == "GAUSSIAN": - return GAUSSIAN - elif kernelTypeString.upper() == "EXPONENTIAL": - return EXPONENTIAL - elif kernelTypeString.upper() == "POLYNOMIAL": - return POLYNOMIAL - elif kernelTypeString.upper() == "LOGINVERSEDISTANCE": - return LOGINVERSEDISTANCE - elif kernelTypeString.upper() == "MONOMIAL": - return MONOMIAL - elif kernelTypeString.upper() == "GREENS_2D": - return GREENS_2D - elif kernelTypeString.upper() == "MANIFOLD_FRACTIONAL": - return MANIFOLD_FRACTIONAL - else: - raise NotImplementedError(kernelTypeString) - - -cdef REAL_t fracKernelFinite1D(REAL_t *x, REAL_t *y, void *c_params): - cdef: - REAL_t s, C, d2, inter - twoPointFunction interaction = (((c_params+fINTERACTION))[0]) - interaction.evalPtr(1, x, y, &inter) - if inter != 0.: - s = getREAL(c_params, fS) - C = getREAL(c_params, fSCALING) - d2 = interaction.dist2 - return C*pow(d2, -0.5-s) - else: - return 0. - - -cdef REAL_t fracKernelFinite2D(REAL_t *x, REAL_t *y, void *c_params): - cdef: - REAL_t s, C, d2, inter - twoPointFunction interaction = (((c_params+fINTERACTION))[0]) - interaction.evalPtr(2, x, y, &inter) - if inter != 0.: - s = getREAL(c_params, fS) - C = getREAL(c_params, fSCALING) - d2 = interaction.dist2 - return C*pow(d2, -1.-s) - else: - return 0. - - -cdef REAL_t fracKernelFinite3D(REAL_t *x, REAL_t *y, void *c_params): - cdef: - REAL_t s, C, d2, inter - twoPointFunction interaction = (((c_params+fINTERACTION))[0]) - interaction.evalPtr(3, x, y, &inter) - if inter != 0.: - s = getREAL(c_params, fS) - C = getREAL(c_params, fSCALING) - d2 = interaction.dist2 - return C*pow(d2, -1.5-s) - else: - return 0. - - -cdef REAL_t fracKernelFinite1Dboundary(REAL_t *x, REAL_t *y, void *c_params): - cdef: - REAL_t s, C, d2, inter - twoPointFunction interaction = (((c_params+fINTERACTION))[0]) - interaction.evalPtr(1, x, y, &inter) - if inter != 0.: - s = getREAL(c_params, fS) - C = getREAL(c_params, fSCALING) - d2 = interaction.dist2 - return C*pow(d2, -s) - else: - return 0. - - -cdef REAL_t fracKernelFinite2Dboundary(REAL_t *x, REAL_t *y, void *c_params): - cdef: - REAL_t s, C, d2, inter - twoPointFunction interaction = (((c_params+fINTERACTION))[0]) - interaction.evalPtr(2, x, y, &inter) - if inter != 0.: - s = getREAL(c_params, fS) - C = getREAL(c_params, fSCALING) - d2 = interaction.dist2 - return C*pow(d2, -0.5-s) - else: - return 0. - - -cdef REAL_t fracKernelFinite3Dboundary(REAL_t *x, REAL_t *y, void *c_params): - cdef: - REAL_t s, C, d2, inter - twoPointFunction interaction = (((c_params+fINTERACTION))[0]) - interaction.evalPtr(3, x, y, &inter) - if inter != 0.: - s = getREAL(c_params, fS) - C = getREAL(c_params, fSCALING) - d2 = interaction.dist2 - return C*pow(d2, -1.0-s) - else: - return 0. - - -cdef REAL_t fracKernelInfinite1D(REAL_t *x, REAL_t *y, void *c_params): - cdef: - REAL_t s = getREAL(c_params, fS) - REAL_t C = getREAL(c_params, fSCALING) - REAL_t d2 - d2 = (x[0]-y[0])*(x[0]-y[0]) - return C*pow(d2, -0.5-s) - - -cdef REAL_t fracKernelInfinite2D(REAL_t *x, REAL_t *y, void *c_params): - cdef: - REAL_t s = getREAL(c_params, fS) - REAL_t C = getREAL(c_params, fSCALING) - REAL_t d2 - d2 = (x[0]-y[0])*(x[0]-y[0]) + (x[1]-y[1])*(x[1]-y[1]) - return C*pow(d2, -1.-s) - - -cdef REAL_t fracKernelInfinite3D(REAL_t *x, REAL_t *y, void *c_params): - cdef: - REAL_t s = getREAL(c_params, fS) - REAL_t C = getREAL(c_params, fSCALING) - REAL_t d2 - d2 = (x[0]-y[0])*(x[0]-y[0]) + (x[1]-y[1])*(x[1]-y[1]) + (x[2]-y[2])*(x[2]-y[2]) - return C*pow(d2, -1.5-s) - - -cdef REAL_t temperedFracKernelInfinite1D(REAL_t *x, REAL_t *y, void *c_params): - cdef: - REAL_t s = getREAL(c_params, fS) - REAL_t C = getREAL(c_params, fSCALING) - REAL_t lam = getREAL(c_params, fTEMPERED) - REAL_t d2 - d2 = (x[0]-y[0])*(x[0]-y[0]) - return C*pow(d2, -0.5-s)*exp(-lam*sqrt(d2)) - - -cdef REAL_t temperedFracKernelInfinite2D(REAL_t *x, REAL_t *y, void *c_params): - cdef: - REAL_t s = getREAL(c_params, fS) - REAL_t C = getREAL(c_params, fSCALING) - REAL_t lam = getREAL(c_params, fTEMPERED) - REAL_t d2 - d2 = (x[0]-y[0])*(x[0]-y[0]) + (x[1]-y[1])*(x[1]-y[1]) - return C*pow(d2, -1.-s)*exp(-lam*sqrt(d2)) - - -cdef REAL_t temperedFracKernelInfinite3D(REAL_t *x, REAL_t *y, void *c_params): - cdef: - REAL_t s = getREAL(c_params, fS) - REAL_t C = getREAL(c_params, fSCALING) - REAL_t lam = getREAL(c_params, fTEMPERED) - REAL_t d2 - d2 = (x[0]-y[0])*(x[0]-y[0]) + (x[1]-y[1])*(x[1]-y[1]) + (x[2]-y[2])*(x[2]-y[2]) - return C*pow(d2, -1.5-s)*exp(-lam*sqrt(d2)) - - -cdef REAL_t fracKernelInfinite1Dboundary(REAL_t *x, REAL_t *y, void *c_params): - cdef: - REAL_t s = getREAL(c_params, fS) - REAL_t C = getREAL(c_params, fSCALING) - REAL_t d2 - d2 = (x[0]-y[0])*(x[0]-y[0]) - return C*pow(d2, -s) - - -cdef REAL_t fracKernelInfinite2Dboundary(REAL_t *x, REAL_t *y, void *c_params): - cdef: - REAL_t s = getREAL(c_params, fS) - REAL_t C = getREAL(c_params, fSCALING) - REAL_t d2 - d2 = (x[0]-y[0])*(x[0]-y[0]) + (x[1]-y[1])*(x[1]-y[1]) - return C*pow(d2, -0.5-s) - - -cdef REAL_t fracKernelInfinite3Dboundary(REAL_t *x, REAL_t *y, void *c_params): - cdef: - REAL_t s = getREAL(c_params, fS) - REAL_t C = getREAL(c_params, fSCALING) - REAL_t d2 - d2 = (x[0]-y[0])*(x[0]-y[0]) + (x[1]-y[1])*(x[1]-y[1]) + (x[2]-y[2])*(x[2]-y[2]) - return C*pow(d2, -1.0-s) - - -cdef REAL_t temperedFracKernelInfinite1Dboundary(REAL_t *x, REAL_t *y, void *c_params): - cdef: - REAL_t s = getREAL(c_params, fS) - REAL_t C = getREAL(c_params, fSCALING) - REAL_t lam = getREAL(c_params, fTEMPERED) - REAL_t d2 - d2 = (x[0]-y[0])*(x[0]-y[0]) - return C*pow(lam, 2*s)*pow(d2, -0.5)*gammainc(-2*s, lam*sqrt(d2)) - - -cdef REAL_t temperedFracKernelInfinite2Dboundary(REAL_t *x, REAL_t *y, void *c_params): - cdef: - REAL_t s = getREAL(c_params, fS) - REAL_t C = getREAL(c_params, fSCALING) - REAL_t lam = getREAL(c_params, fTEMPERED) - REAL_t d2 - d2 = (x[0]-y[0])*(x[0]-y[0]) + (x[1]-y[1])*(x[1]-y[1]) - return C*pow(lam, 2*s)*pow(d2, -1.0)*gammainc(-2*s, lam*sqrt(d2)) - - -cdef REAL_t temperedFracKernelInfinite3Dboundary(REAL_t *x, REAL_t *y, void *c_params): - cdef: - REAL_t s = getREAL(c_params, fS) - REAL_t C = getREAL(c_params, fSCALING) - REAL_t lam = getREAL(c_params, fTEMPERED) - REAL_t d2 - d2 = (x[0]-y[0])*(x[0]-y[0]) + (x[1]-y[1])*(x[1]-y[1]) + (x[2]-y[2])*(x[2]-y[2]) - return C*pow(lam, 2*s)*pow(d2, -1.5)*gammainc(-2*s, lam*sqrt(d2)) - - -cdef REAL_t indicatorKernel1D(REAL_t *x, REAL_t *y, void *c_params): - cdef: - twoPointFunction interaction = (((c_params+fINTERACTION))[0]) - REAL_t C, inter - interaction.evalPtr(1, x, y, &inter) - if inter != 0.: - C = getREAL(c_params, fSCALING) - return C - else: - return 0. - - -cdef REAL_t indicatorKernel2D(REAL_t *x, REAL_t *y, void *c_params): - cdef: - twoPointFunction interaction = (((c_params+fINTERACTION))[0]) - REAL_t C, inter - interaction.evalPtr(2, x, y, &inter) - if inter != 0.: - C = getREAL(c_params, fSCALING) - return C - else: - return 0. - - -cdef REAL_t indicatorKernel1Dboundary(REAL_t *x, REAL_t *y, void *c_params): - cdef: - twoPointFunction interaction = (((c_params+fINTERACTION))[0]) - REAL_t C, inter - interaction.evalPtr(1, x, y, &inter) - if inter != 0.: - C = getREAL(c_params, fSCALING) - return -C*2.0*sqrt((x[0]-y[0])*(x[0]-y[0])) - else: - return 0. - - -cdef REAL_t indicatorKernel2Dboundary(REAL_t *x, REAL_t *y, void *c_params): - cdef: - twoPointFunction interaction = (((c_params+fINTERACTION))[0]) - REAL_t C, inter - interaction.evalPtr(2, x, y, &inter) - if inter != 0.: - C = getREAL(c_params, fSCALING) - return -C*sqrt((x[0]-y[0])*(x[0]-y[0])+(x[1]-y[1])*(x[1]-y[1])) - else: - return 0. - - -cdef REAL_t peridynamicKernel1D(REAL_t *x, REAL_t *y, void *c_params): - cdef: - interactionDomain interaction = (((c_params+fINTERACTION))[0]) - REAL_t C - REAL_t d2, inter - interaction.evalPtr(1, x, y, &inter) - if inter != 0.: - d2 = interaction.dist2 - C = getREAL(c_params, fSCALING) - return C/sqrt(d2) - else: - return 0. - - -cdef REAL_t peridynamicKernel2D(REAL_t *x, REAL_t *y, void *c_params): - cdef: - interactionDomain interaction = (((c_params+fINTERACTION))[0]) - REAL_t C - REAL_t d2, inter - interaction.evalPtr(2, x, y, &inter) - if inter != 0.: - d2 = interaction.dist2 - C = getREAL(c_params, fSCALING) - return C/sqrt(d2) - else: - return 0. - - -cdef REAL_t peridynamicKernel3D(REAL_t *x, REAL_t *y, void *c_params): - cdef: - interactionDomain interaction = (((c_params+fINTERACTION))[0]) - REAL_t C - REAL_t d2, inter - interaction.evalPtr(3, x, y, &inter) - if inter != 0.: - d2 = interaction.dist2 - C = getREAL(c_params, fSCALING) - return C/sqrt(d2) - else: - return 0. - - -cdef REAL_t peridynamicKernel1Dboundary(REAL_t *x, REAL_t *y, void *c_params): - cdef: - interactionDomain interaction = (((c_params+fINTERACTION))[0]) - REAL_t C - REAL_t d, inter - interaction.evalPtr(1, x, y, &inter) - if inter != 0.: - d2 = interaction.dist2 - C = getREAL(c_params, fSCALING) - return -2.0*C*0.5*log(d2) - else: - return 0. - - -cdef REAL_t peridynamicKernel2Dboundary(REAL_t *x, REAL_t *y, void *c_params): - cdef: - interactionDomain interaction = (((c_params+fINTERACTION))[0]) - REAL_t C, inter - interaction.evalPtr(2, x, y, &inter) - if inter != 0.: - C = getREAL(c_params, fSCALING) - return -2.0*C - else: - return 0. - -cdef REAL_t gaussianKernel1D(REAL_t *x, REAL_t *y, void *c_params): - cdef: - interactionDomain interaction = (((c_params+fINTERACTION))[0]) - REAL_t C, invD - REAL_t d2, inter - interaction.evalPtr(1, x, y, &inter) - if inter != 0.: - d2 = interaction.dist2 - C = getREAL(c_params, fSCALING) - invD = getREAL(c_params, fEXPONENTINVERSE) - return C*exp(-d2*invD) - else: - return 0. - - -cdef REAL_t gaussianKernel2D(REAL_t *x, REAL_t *y, void *c_params): - cdef: - interactionDomain interaction = (((c_params+fINTERACTION))[0]) - REAL_t C, invD - REAL_t d2, inter - interaction.evalPtr(2, x, y, &inter) - if inter != 0.: - d2 = interaction.dist2 - C = getREAL(c_params, fSCALING) - invD = getREAL(c_params, fEXPONENTINVERSE) - return C*exp(-d2*invD) - else: - return 0. - - -cdef REAL_t gaussianKernel1Dboundary(REAL_t *x, REAL_t *y, void *c_params): - cdef: - interactionDomain interaction = (((c_params+fINTERACTION))[0]) - REAL_t C, invD - REAL_t d2, inter - interaction.evalPtr(1, x, y, &inter) - if inter != 0.: - d2 = interaction.dist2 - C = getREAL(c_params, fSCALING) - invD = getREAL(c_params, fEXPONENTINVERSE) - return C*sqrt(1./(d2*invD))*gammainc(0.5, d2*invD)*sqrt(d2) - else: - return 0. - - -cdef REAL_t gaussianKernel2Dboundary(REAL_t *x, REAL_t *y, void *c_params): - cdef: - interactionDomain interaction = (((c_params+fINTERACTION))[0]) - REAL_t C, invD - REAL_t d2, inter - interaction.evalPtr(2, x, y, &inter) - if inter != 0.: - d2 = interaction.dist2 - C = getREAL(c_params, fSCALING) - invD = getREAL(c_params, fEXPONENTINVERSE) - return C*(1./(d2*invD))*gammainc(1.0, d2*invD)*sqrt(d2) - else: - return 0. - - -cdef REAL_t exponentialKernel(REAL_t *x, REAL_t *y, void *c_params): - cdef: - interactionDomain interaction = (((c_params+fINTERACTION))[0]) - INDEX_t dim = getINDEX(c_params, fKDIM) - REAL_t C, a - REAL_t d2, inter - interaction.evalPtr(dim, x, y, &inter) - if inter != 0.: - d2 = interaction.dist2 - C = getREAL(c_params, fSCALING) - a = getREAL(c_params, fEXPONENTINVERSE) - return C*exp(-a*sqrt(d2)) - else: - return 0. - - -cdef REAL_t exponentialKernelBoundary(REAL_t *x, REAL_t *y, void *c_params): - cdef: - interactionDomain interaction = (((c_params+fINTERACTION))[0]) - INDEX_t dim = getINDEX(c_params, fKDIM) - REAL_t C, a - REAL_t d2, inter - interaction.evalPtr(dim, x, y, &inter) - if inter != 0.: - d2 = interaction.dist2 - C = getREAL(c_params, fSCALING) - a = getREAL(c_params, fEXPONENTINVERSE) - return 2.0*C*exp(-a*sqrt(d2))/a - else: - return 0. - - -cdef REAL_t polynomialKernel1D(REAL_t *x, REAL_t *y, void *c_params): - cdef: - interactionDomain interaction = (((c_params+fINTERACTION))[0]) - REAL_t C, a - REAL_t d2, inter - interaction.evalPtr(1, x, y, &inter) - if inter != 0.: - d2 = interaction.dist2 - C = getREAL(c_params, fSCALING) - a = getREAL(c_params, fEXPONENTINVERSE) - return C*(a**3*d2)/(a**2+d2)**2 - else: - return 0. - - -cdef REAL_t polynomialKernel1Dboundary(REAL_t *x, REAL_t *y, void *c_params): - cdef: - interactionDomain interaction = (((c_params+fINTERACTION))[0]) - REAL_t C, a - REAL_t d2, inter - interaction.evalPtr(1, x, y, &inter) - if inter != 0.: - d2 = interaction.dist2 - C = getREAL(c_params, fSCALING) - a = getREAL(c_params, fEXPONENTINVERSE) - return C*(-a**2/(2*sqrt(d2)) + a**3/2/(a**2+d2)) - else: - return 0. - - -cdef REAL_t logInverseDistance2D(REAL_t *x, REAL_t *y, void *c_params): - cdef: - REAL_t C = getREAL(c_params, fSCALING) - REAL_t d2 - d2 = (x[0]-y[0])*(x[0]-y[0]) + (x[1]-y[1])*(x[1]-y[1]) - C = getREAL(c_params, fSCALING) - return -0.5*C*log(d2) - - -cdef COMPLEX_t greens2Dcomplex(REAL_t *x, REAL_t *y, void *c_params): - cdef: - REAL_t C = getREAL(c_params, fSCALING) - REAL_t lam = getREAL(c_params, fGREENS_LAMBDA) - REAL_t d2 - d2 = (x[0]-y[0])*(x[0]-y[0]) + (x[1]-y[1])*(x[1]-y[1]) - C = getREAL(c_params, fSCALING) - return C*hankel10complex(lam*sqrt(d2)) - - -cdef COMPLEX_t greens3Dcomplex(REAL_t *x, REAL_t *y, void *c_params): - cdef: - REAL_t C = getREAL(c_params, fSCALING) - COMPLEX_t lam = getCOMPLEX(c_params, fGREENS_LAMBDA) - REAL_t d2 - d2 = (x[0]-y[0])*(x[0]-y[0]) + (x[1]-y[1])*(x[1]-y[1]) + (x[2]-y[2])*(x[2]-y[2]) - C = getREAL(c_params, fSCALING) - d2 = sqrt(d2) - return C*exp(-lam.real*d2)*(cos(-lam.imag*d2)+1j*sin(-lam.imag*d2))/d2 - - -cdef REAL_t monomial3D(REAL_t *x, REAL_t *y, void *c_params): - cdef: - REAL_t C = getREAL(c_params, fSCALING) - REAL_t singularityValue = getREAL(c_params, fSINGULARITY) - REAL_t d2 - d2 = (x[0]-y[0])*(x[0]-y[0]) + (x[1]-y[1])*(x[1]-y[1]) + (x[2]-y[2])*(x[2]-y[2]) - C = getREAL(c_params, fSCALING) - return C*pow(d2, 0.5*singularityValue) - - -cdef REAL_t updateAndEvalIntegrable(REAL_t *x, REAL_t *y, void *c_params): - cdef: - INDEX_t dim = getINDEX(c_params, fKDIM) - REAL_t[::1] xA - function horizonFun - twoPointFunction scalingFun - REAL_t horizon, C - fun_t kernel = getFun(c_params, fEVAL) - BOOL_t horizonFunNull = isNull(c_params, fHORIZONFUN) - BOOL_t scalingFunNull = isNull(c_params, fSCALINGFUN) - if not horizonFunNull or not scalingFunNull: - xA = x - if not horizonFunNull: - horizonFun = (((c_params+fHORIZONFUN))[0]) - horizon = horizonFun.eval(xA) - setREAL(c_params, fHORIZON2, horizon*horizon) - if not scalingFunNull: - scalingFun = (((c_params+fSCALINGFUN))[0]) - scalingFun.evalPtr(dim, x, y, &C) - setREAL(c_params, fSCALING, C) - return kernel(x, y, c_params) - - -cdef COMPLEX_t updateAndEvalIntegrableComplex(REAL_t *x, REAL_t *y, void *c_params): - cdef: - INDEX_t dim = getINDEX(c_params, fKDIM) - REAL_t[::1] xA - function horizonFun - twoPointFunction scalingFun - REAL_t horizon, C - complex_fun_t kernel = getComplexFun(c_params, fEVAL) - BOOL_t horizonFunNull = isNull(c_params, fHORIZONFUN) - BOOL_t scalingFunNull = isNull(c_params, fSCALINGFUN) - if not horizonFunNull or not scalingFunNull: - xA = x - if not horizonFunNull: - horizonFun = (((c_params+fHORIZONFUN))[0]) - horizon = horizonFun.eval(xA) - setREAL(c_params, fHORIZON2, horizon*horizon) - if not scalingFunNull: - scalingFun = (((c_params+fSCALINGFUN))[0]) - scalingFun.evalPtr(dim, x, y, &C) - setREAL(c_params, fSCALING, C) - return kernel(x, y, c_params) - - -cdef REAL_t updateAndEvalFractional(REAL_t *x, REAL_t *y, void *c_params): - cdef: - INDEX_t dim = getINDEX(c_params, fKDIM) - REAL_t[::1] xA - fractionalOrderBase sFun - function horizonFun - twoPointFunction scalingFun - REAL_t s, horizon, C - BOOL_t boundary - fun_t kernel = getFun(c_params, fEVAL) - - if not isNull(c_params, fORDERFUN): - sFun = (((c_params+fORDERFUN))[0]) - sFun.evalPtr(dim, x, y, &s) - setREAL(c_params, fS, s) - boundary = getBOOL(c_params, fBOUNDARY) - setREAL(c_params, fSINGULARITY, boundary-dim-2*s) - if not isNull(c_params, fHORIZONFUN): - xA = x - horizonFun = (((c_params+fHORIZONFUN))[0]) - horizon = horizonFun.eval(xA) - setREAL(c_params, fHORIZON2, horizon*horizon) - if not isNull(c_params, fSCALINGFUN): - scalingFun = (((c_params+fSCALINGFUN))[0]) - scalingFun.evalPtr(dim, x, y, &C) - setREAL(c_params, fSCALING, C) - return kernel(x, y, c_params) - - -cdef class Kernel(twoPointFunction): - """A kernel functions that can be used to define a nonlocal operator.""" - - def __init__(self, INDEX_t dim, kernelType kType, function horizon, interactionDomain interaction, twoPointFunction scaling, twoPointFunction phi, BOOL_t piecewise=True, BOOL_t boundary=False, INDEX_t valueSize=1, REAL_t max_horizon=np.nan, **kwargs): - cdef: - parametrizedTwoPointFunction parametrizedScaling - int i - - self.c_kernel_params = PyMem_Malloc(NUM_KERNEL_PARAMS*OFFSET) - for i in range(NUM_KERNEL_PARAMS): - ((self.c_kernel_params+i*OFFSET))[0] = NULL - - self.dim = dim - assert valueSize >= 1, "Creation of kernel with valueSize = {}".format(valueSize) - self.valueSize = valueSize - self.kernelType = kType - self.piecewise = piecewise - self.boundary = boundary - - setINDEX(self.c_kernel_params, fKDIM, dim) - - symmetric = isinstance(horizon, constant) and scaling.symmetric and interaction.symmetric and (phi is None or phi.symmetric) - super(Kernel, self).__init__(symmetric, valueSize) - - if self.kernelType == INDICATOR: - self.min_singularity = 0. - self.max_singularity = 0. - self.singularityValue = 0. - elif self.kernelType == PERIDYNAMIC: - self.min_singularity = -1. - self.max_singularity = -1. - self.singularityValue = -1. - elif self.kernelType == GAUSSIAN: - self.min_singularity = 0. - self.max_singularity = 0. - self.singularityValue = 0. - elif self.kernelType == EXPONENTIAL: - self.min_singularity = 0. - self.max_singularity = 0. - self.singularityValue = 0. - elif self.kernelType == POLYNOMIAL: - self.min_singularity = 0. - self.max_singularity = 0. - self.singularityValue = 0. - elif self.kernelType == LOGINVERSEDISTANCE: - self.min_singularity = 0. - self.max_singularity = 0. - self.singularityValue = 0. - elif self.kernelType == MONOMIAL: - monomialPower = kwargs.get('monomialPower', np.nan) - self.min_singularity = monomialPower - self.max_singularity = monomialPower - self.singularityValue = monomialPower - - self.horizon = horizon - self.variableHorizon = not isinstance(self.horizon, constant) - if self.variableHorizon: - self.horizonValue2 = np.nan - self.finiteHorizon = True - ((self.c_kernel_params+fHORIZONFUN))[0] = horizon - self.max_horizon = max_horizon - else: - self.horizonValue = self.horizon.value - self.max_horizon = self.horizon.value - self.finiteHorizon = self.horizon.value != np.inf - if self.kernelType == GAUSSIAN: - if self.finiteHorizon: - setREAL(self.c_kernel_params, fEXPONENTINVERSE, 1.0/(self.horizonValue/3.)**2) - else: - variance = kwargs.get('variance', 1.0) - setREAL(self.c_kernel_params, fEXPONENTINVERSE, 0.5/variance**self.dim) - elif self.kernelType == EXPONENTIAL: - exponentialRate = kwargs.get('exponentialRate', 1.0) - setREAL(self.c_kernel_params, fEXPONENTINVERSE, exponentialRate) - elif self.kernelType == POLYNOMIAL: - a = kwargs.get('a', 1.0) - setREAL(self.c_kernel_params, fEXPONENTINVERSE, a) - - self.interaction = interaction - self.complement = self.interaction.complement - ((self.c_kernel_params+fINTERACTION))[0] = self.interaction - self.interaction.setParams(self.c_kernel_params) - - self.phi = phi - self.scalingPrePhi = scaling - if phi is not None: - scaling = phi*scaling - self.scaling = scaling - self.variableScaling = not isinstance(self.scaling, (constantFractionalLaplacianScaling, constantTwoPoint)) - if self.variableScaling: - if isinstance(self.scaling, parametrizedTwoPointFunction): - parametrizedScaling = self.scaling - parametrizedScaling.setParams(self.c_kernel_params) - self.scalingValue = np.nan - ((self.c_kernel_params+fSCALINGFUN))[0] = self.scaling - else: - self.scalingValue = self.scaling.value - - self.variable = self.variableHorizon or self.variableScaling - - if self.piecewise: - if not self.boundary: - if dim == 1: - if self.kernelType == INDICATOR: - self.kernelFun = indicatorKernel1D - elif self.kernelType == PERIDYNAMIC: - self.kernelFun = peridynamicKernel1D - elif self.kernelType == GAUSSIAN: - self.kernelFun = gaussianKernel1D - elif self.kernelType == EXPONENTIAL: - self.kernelFun = exponentialKernel - elif self.kernelType == POLYNOMIAL: - self.kernelFun = polynomialKernel1D - elif dim == 2: - if self.kernelType == INDICATOR: - self.kernelFun = indicatorKernel2D - elif self.kernelType == PERIDYNAMIC: - self.kernelFun = peridynamicKernel2D - elif self.kernelType == GAUSSIAN: - self.kernelFun = gaussianKernel2D - elif self.kernelType == EXPONENTIAL: - self.kernelFun = exponentialKernel - elif self.kernelType == POLYNOMIAL: - raise NotImplementedError() - elif self.kernelType == LOGINVERSEDISTANCE: - self.kernelFun = logInverseDistance2D - elif dim == 3: - if self.kernelType == PERIDYNAMIC: - self.kernelFun = peridynamicKernel3D - elif self.kernelType == MONOMIAL: - self.kernelFun = monomial3D - elif self.kernelType == GAUSSIAN: - raise NotImplementedError() - elif self.kernelType == EXPONENTIAL: - self.kernelFun = exponentialKernel - elif self.kernelType == POLYNOMIAL: - raise NotImplementedError() - else: - raise NotImplementedError() - else: - if dim == 1: - if self.kernelType == INDICATOR: - self.kernelFun = indicatorKernel1Dboundary - elif self.kernelType == PERIDYNAMIC: - self.kernelFun = peridynamicKernel1Dboundary - elif self.kernelType == GAUSSIAN: - self.kernelFun = gaussianKernel1Dboundary - elif self.kernelType == EXPONENTIAL: - self.kernelFun = exponentialKernelBoundary - elif self.kernelType == POLYNOMIAL: - self.kernelFun = polynomialKernel1Dboundary - elif dim == 2: - if self.kernelType == INDICATOR: - self.kernelFun = indicatorKernel2Dboundary - elif self.kernelType == PERIDYNAMIC: - self.kernelFun = peridynamicKernel2Dboundary - elif self.kernelType == GAUSSIAN: - self.kernelFun = gaussianKernel2Dboundary - elif self.kernelType == EXPONENTIAL: - self.kernelFun = exponentialKernelBoundary - elif self.kernelType == POLYNOMIAL: - raise NotImplementedError() - elif dim == 3: - if self.kernelType == GAUSSIAN: - raise NotImplementedError() - elif self.kernelType == EXPONENTIAL: - self.kernelFun = exponentialKernelBoundary - elif self.kernelType == POLYNOMIAL: - raise NotImplementedError() - else: - raise NotImplementedError() - else: - self.kernelFun = updateAndEvalIntegrable - - if not self.boundary: - if dim == 1: - if self.kernelType == INDICATOR: - setFun(self.c_kernel_params, fEVAL, indicatorKernel1D) - elif self.kernelType == PERIDYNAMIC: - setFun(self.c_kernel_params, fEVAL, peridynamicKernel1D) - elif self.kernelType == GAUSSIAN: - setFun(self.c_kernel_params, fEVAL, gaussianKernel1D) - elif self.kernelType == EXPONENTIAL: - setFun(self.c_kernel_params, fEVAL, exponentialKernel) - elif self.kernelType == POLYNOMIAL: - setFun(self.c_kernel_params, fEVAL, polynomialKernel1D) - elif dim == 2: - if self.kernelType == INDICATOR: - setFun(self.c_kernel_params, fEVAL, indicatorKernel2D) - elif self.kernelType == PERIDYNAMIC: - setFun(self.c_kernel_params, fEVAL, peridynamicKernel2D) - elif self.kernelType == GAUSSIAN: - setFun(self.c_kernel_params, fEVAL, gaussianKernel2D) - elif self.kernelType == EXPONENTIAL: - setFun(self.c_kernel_params, fEVAL, exponentialKernel) - elif self.kernelType == POLYNOMIAL: - raise NotImplementedError() - elif self.kernelType == LOGINVERSEDISTANCE: - setFun(self.c_kernel_params, fEVAL, logInverseDistance2D) - elif dim == 3: - if self.kernelType == PERIDYNAMIC: - setFun(self.c_kernel_params, fEVAL, peridynamicKernel3D) - elif self.kernelType == MONOMIAL: - setFun(self.c_kernel_params, fEVAL, monomial3D) - elif self.kernelType == GAUSSIAN: - raise NotImplementedError() - elif self.kernelType == EXPONENTIAL: - setFun(self.c_kernel_params, fEVAL, exponentialKernel) - elif self.kernelType == POLYNOMIAL: - raise NotImplementedError() - else: - raise NotImplementedError() - else: - if dim == 1: - if self.kernelType == INDICATOR: - setFun(self.c_kernel_params, fEVAL, indicatorKernel1Dboundary) - elif self.kernelType == PERIDYNAMIC: - setFun(self.c_kernel_params, fEVAL, peridynamicKernel1Dboundary) - elif self.kernelType == GAUSSIAN: - setFun(self.c_kernel_params, fEVAL, gaussianKernel1Dboundary) - elif self.kernelType == EXPONENTIAL: - setFun(self.c_kernel_params, fEVAL, exponentialKernelBoundary) - elif self.kernelType == POLYNOMIAL: - setFun(self.c_kernel_params, fEVAL, polynomialKernel1Dboundary) - elif dim == 2: - if self.kernelType == INDICATOR: - setFun(self.c_kernel_params, fEVAL, indicatorKernel2Dboundary) - elif self.kernelType == PERIDYNAMIC: - setFun(self.c_kernel_params, fEVAL, peridynamicKernel2Dboundary) - elif self.kernelType == GAUSSIAN: - setFun(self.c_kernel_params, fEVAL, gaussianKernel2Dboundary) - elif self.kernelType == EXPONENTIAL: - setFun(self.c_kernel_params, fEVAL, exponentialKernelBoundary) - elif self.kernelType == POLYNOMIAL: - raise NotImplementedError() - elif dim == 3: - if self.kernelType == GAUSSIAN: - raise NotImplementedError() - elif self.kernelType == EXPONENTIAL: - setFun(self.c_kernel_params, fEVAL, exponentialKernelBoundary) - elif self.kernelType == POLYNOMIAL: - raise NotImplementedError() - else: - raise NotImplementedError() - - def getParamPtrAddr(self): - return self.c_kernel_params - - @property - def boundary(self): - "The order of the boundary." - return getBOOL(self.c_kernel_params, fBOUNDARY) - - @boundary.setter - def boundary(self, BOOL_t boundary): - setBOOL(self.c_kernel_params, fBOUNDARY, boundary) - - cdef BOOL_t getBoundary(self): - return getBOOL(self.c_kernel_params, fBOUNDARY) - - cdef void setBoundary(self, BOOL_t boundary): - setBOOL(self.c_kernel_params, fBOUNDARY, boundary) - - @property - def singularityValue(self): - "The order of the singularity." - return getREAL(self.c_kernel_params, fSINGULARITY) - - @singularityValue.setter - def singularityValue(self, REAL_t singularity): - setREAL(self.c_kernel_params, fSINGULARITY, singularity) - - cdef REAL_t getSingularityValue(self): - return getREAL(self.c_kernel_params, fSINGULARITY) - - cdef void setSingularityValue(self, REAL_t singularity): - setREAL(self.c_kernel_params, fSINGULARITY, singularity) - - @property - def horizonValue(self): - "The value of the interaction horizon." - return sqrt(getREAL(self.c_kernel_params, fHORIZON2)) - - @horizonValue.setter - def horizonValue(self, REAL_t horizon): - setREAL(self.c_kernel_params, fHORIZON2, horizon**2) - - cdef REAL_t getHorizonValue(self): - return sqrt(getREAL(self.c_kernel_params, fHORIZON2)) - - cdef void setHorizonValue(self, REAL_t horizon): - setREAL(self.c_kernel_params, fHORIZON2, horizon**2) - - @property - def horizonValue2(self): - return getREAL(self.c_kernel_params, fHORIZON2) - - cdef REAL_t getHorizonValue2(self): - return getREAL(self.c_kernel_params, fHORIZON2) - - @horizonValue2.setter - def horizonValue2(self, REAL_t horizon2): - setREAL(self.c_kernel_params, fHORIZON2, horizon2) - - @property - def scalingValue(self): - "The value of the scaling factor." - return getREAL(self.c_kernel_params, fSCALING) - - @scalingValue.setter - def scalingValue(self, REAL_t scaling): - setREAL(self.c_kernel_params, fSCALING, scaling) - - cdef REAL_t getScalingValue(self): - return getREAL(self.c_kernel_params, fSCALING) - - cdef void setScalingValue(self, REAL_t scaling): - setREAL(self.c_kernel_params, fSCALING, scaling) - - def getLongDescription(self): - cdef: - str descr = '' - dict params = {} - params['d'] = self.dim - if self.finiteHorizon: - params['\\delta'] = self.horizon - if self.kernelType == INDICATOR: - descr = '' - elif self.kernelType == PERIDYNAMIC: - descr = '\\frac{1}{|x-y|}' - elif self.kernelType == FRACTIONAL: - descr = '\\frac{1}{|x-y|^{d+2s}}' - elif self.kernelType == MANIFOLD_FRACTIONAL: - descr = '(\\zeta(d+2s, d(x, y)) + \\zeta(d+2s, 1-d(x, y)))' - elif self.kernelType == GAUSSIAN: - if self.finiteHorizon: - exponentInverse = self.getKernelParam('exponentInverse') - params['a'] = exponentInverse - descr = '\\exp(-a |x-y|^2)' - else: - variance = self.getKernelParam('variance') - params['\\sigma'] = variance - descr = '\\exp(-0.5 |x-y|^2/\\sigma^{d})' - elif self.kernelType == EXPONENTIAL: - exponentialRate = self.getKernelParam('exponentialRate') - params['a'] = exponentialRate - descr = '\\exp(-a |x-y|)' - elif self.kernelType == POLYNOMIAL: - a = self.getKernelParam('a') - params['a'] = a - descr = '\\frac{a^3 |x-y|^2}{(a^2 + |x-y|^2)^2}' - paramsStr = ', '.join([k+'='+str(v) for k, v in params.items()]) - return self.scaling.getLongDescription() + ' ' + descr + ' ' + self.interaction.getLongDescription() + ', ' + paramsStr - - def getKernelParams(self): - params = [] - if self.kernelType == GAUSSIAN: - if self.finiteHorizon: - params.append('exponentInverse') - else: - params.append('variance') - elif self.kernelType == EXPONENTIAL: - params.append('exponentialRate') - elif self.kernelType == POLYNOMIAL: - params.append('a') - elif self.kernelType == MONOMIAL: - params.append('monomialPower') - return params - - def getKernelParam(self, str param): - if self.kernelType == GAUSSIAN: - if self.finiteHorizon: - if param == 'exponentInverse': - return getREAL(self.c_kernel_params, fEXPONENTINVERSE) - else: - if param == 'variance': - exponentInverse = getREAL(self.c_kernel_params, fEXPONENTINVERSE) - variance = 1.0/(2.0*exponentInverse)**(1.0/self.dim) - return variance - elif self.kernelType == EXPONENTIAL: - if param == 'exponentialRate': - return getREAL(self.c_kernel_params, fEXPONENTINVERSE) - elif self.kernelType == POLYNOMIAL: - if param == 'a': - return getREAL(self.c_kernel_params, fEXPONENTINVERSE) - elif self.kernelType == MONOMIAL: - if param == 'monomialPower': - return self.getSingularityValue() - raise NotImplementedError("Parameter not available: {}".format(param)) - - cdef void evalParamsOnSimplices(self, REAL_t[::1] center1, REAL_t[::1] center2, REAL_t[:, ::1] simplex1, REAL_t[:, ::1] simplex2): - # Set the horizon. - if self.variableHorizon: - self.horizonValue = self.horizon.eval(center1) - - cdef void evalParams(self, REAL_t[::1] x, REAL_t[::1] y): - cdef: - REAL_t scalingValue - if self.piecewise: - if self.variableHorizon: - self.horizonValue = self.horizon.eval(x) - if self.kernelType == GAUSSIAN: - setREAL(self.c_kernel_params, fEXPONENTINVERSE, 1.0/(self.horizonValue/3.)**2) - elif self.kernelType == POLYNOMIAL: - setREAL(self.c_kernel_params, fEXPONENTINVERSE, 1.0/(self.horizonValue/3.)**2) - if self.variableScaling: - self.scaling.evalPtr(x.shape[0], &x[0], &y[0], &scalingValue) - self.scalingValue = scalingValue - - def evalParams_py(self, REAL_t[::1] x, REAL_t[::1] y): - "Evaluate the kernel parameters." - cdef: - REAL_t scalingValue - if self.piecewise: - self.evalParams(x, y) - else: - if self.variableHorizon: - self.horizonValue = self.horizon.eval(x) - if self.kernelType == GAUSSIAN: - setREAL(self.c_kernel_params, fEXPONENTINVERSE, 1.0/(self.horizonValue/3.)**2) - elif self.kernelType == POLYNOMIAL: - setREAL(self.c_kernel_params, fEXPONENTINVERSE, 1.0/(self.horizonValue/3.)**2) - if self.variableScaling: - self.scaling.evalPtr(x.shape[0], &x[0], &y[0], &scalingValue) - self.scalingValue = scalingValue - - cdef void evalParamsPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y): - cdef: - REAL_t[::1] xA - REAL_t scalingValue - if self.piecewise: - if self.variableHorizon: - xA = x - self.horizonValue = self.horizon.eval(xA) - if self.kernelType == GAUSSIAN: - setREAL(self.c_kernel_params, fEXPONENTINVERSE, 1.0/(self.horizonValue/3.)**2) - elif self.kernelType == POLYNOMIAL: - setREAL(self.c_kernel_params, fEXPONENTINVERSE, 1.0/(self.horizonValue/3.)**2) - if self.variableScaling: - self.scaling.evalPtr(dim, x, y, &scalingValue) - self.scalingValue = scalingValue - - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] vec): - vec[0] = self.kernelFun(&x[0], &y[0], self.c_kernel_params) - - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* vec): - vec[0] = self.kernelFun(x, y, self.c_kernel_params) - - def __call__(self, REAL_t[::1] x, REAL_t[::1] y, BOOL_t callEvalParams=True): - "Evaluate the kernel." - if self.piecewise and callEvalParams: - self.evalParams(x, y) - return self.kernelFun(&x[0], &y[0], self.c_kernel_params) - - def eval_py(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] vec, BOOL_t callEvalParams=True): - "Evaluate the kernel." - if self.piecewise and callEvalParams: - self.evalParams(x, y) - self.eval(x, y, vec) - - def getModifiedKernel(self, - function horizon=None, - twoPointFunction scaling=None): - cdef: - Kernel newKernel - if horizon is None: - horizon = self.horizon - interaction = self.interaction - else: - if scaling is None and isinstance(self.scaling, (variableFractionalLaplacianScaling, - variableIntegrableScaling)): - scaling = self.scaling.getScalingWithDifferentHorizon() - interaction = deepcopy(self.interaction) - if scaling is None: - scaling = self.scaling - from . kernels import getKernel - newKernel = getKernel(dim=self.dim, kernel=self.kernelType, horizon=horizon, interaction=interaction, scaling=scaling, piecewise=self.piecewise) - setREAL(newKernel.c_kernel_params, fEXPONENTINVERSE, getREAL(self.c_kernel_params, fEXPONENTINVERSE)) - if isinstance(newKernel.scaling, parametrizedTwoPointFunction): - assert newKernel.getParamPtrAddr() == newKernel.scaling.getParamPtrAddr() - if isinstance(self.scaling, parametrizedTwoPointFunction): - assert self.getParamPtrAddr() == self.scaling.getParamPtrAddr() - return newKernel - - def getComplementKernel(self): - "Get the complement kernel." - raise NotImplementedError() - from . kernels import getKernel - newKernel = getKernel(dim=self.dim, kernel=self.kernelType, horizon=self.horizon, interaction=self.interaction.getComplement(), scaling=self.scaling, piecewise=self.piecewise) - return newKernel - - def __repr__(self): - extraInfo = {} - for p in self.getKernelParams(): - extraInfo[p] = self.getKernelParam(p) - if self.kernelType == INDICATOR: - kernelName = 'indicator' - elif self.kernelType == PERIDYNAMIC: - kernelName = 'peridynamic' - elif self.kernelType == GAUSSIAN: - kernelName = 'Gaussian' - elif self.kernelType == EXPONENTIAL: - kernelName = 'exponential' - elif self.kernelType == POLYNOMIAL: - kernelName = 'polynomial' - elif self.kernelType == LOGINVERSEDISTANCE: - kernelName = 'logInverseDistance' - elif self.kernelType == MONOMIAL: - kernelName = 'monomial' - else: - raise NotImplementedError() - if len(extraInfo) > 0: - extraInfo = ', '.join([str(k)+'='+str(v) for k, v in extraInfo.items()]) - return "{}({}{}, {}, {}, {})".format(self.__class__.__name__, kernelName, '' if not self.boundary else '-boundary', repr(self.interaction), self.scaling, extraInfo) - else: - return "{}({}{}, {}, {})".format(self.__class__.__name__, kernelName, '' if not self.boundary else '-boundary', repr(self.interaction), self.scaling) - - def __reduce__(self): - return Kernel, (self.dim, self.kernelType, self.horizon, self.interaction, self.scalingPrePhi, self.phi, self.piecewise, self.boundary, self.valueSize) - - def plot(self, x0=None): - "Plot the kernel function." - from matplotlib import ticker - import matplotlib.pyplot as plt - if x0 is None: - x0 = np.zeros((self.dim), dtype=REAL) - self.evalParams(x0, x0) - if self.finiteHorizon: - delta = self.horizonValue - else: - delta = 2. - x = np.linspace(-1.1*delta, 1.1*delta, 201) - if self.dim == 1: - vals = np.zeros_like(x) - for i in range(x.shape[0]): - y = x0+np.array([x[i]], dtype=REAL) - if np.linalg.norm(x0-y) > 1e-9 or self.singularityValue >= 0: - vals[i] = self(x0, y) - else: - vals[i] = np.nan - plt.plot(x, vals) - plt.yscale('log') - if not self.finiteHorizon: - plt.xlim([x[0], x[x.shape[0]-1]]) - if self.singularityValue < 0: - plt.ylim(top=np.nanmax(vals)) - plt.xlabel('$x-y$') - elif self.dim == 2: - X, Y = np.meshgrid(x, x) - Z = np.zeros_like(X) - for i in range(x.shape[0]): - for j in range(x.shape[0]): - y = x0+np.array([x[i], x[j]], dtype=REAL) - if np.linalg.norm(x0-y) > 1e-9 or self.singularityValue >= 0: - Z[j, i] = self(x0, y) - else: - Z[j, i] = np.nan - levels = np.logspace(np.log10(Z[np.absolute(Z)>0].min()), - np.log10(Z[np.absolute(Z)>0].max()), 10) - if levels[0] < levels[levels.shape[0]-1]: - plt.contourf(X, Y, Z, locator=ticker.LogLocator(), - levels=levels) - else: - plt.contourf(X, Y, Z) - plt.axis('equal') - plt.colorbar() - plt.xlabel('$y_1-x_1$') - plt.ylabel('$y_2-x_2$') - - def getBoundaryKernel(self): - "Get the boundary kernel. This is the kernel that corresponds to the elimination of a subdomain via Gauss theorem." - cdef: - Kernel newKernel - scaling = deepcopy(self.scaling) - if self.phi is not None: - phi = deepcopy(self.phi) - else: - phi = None - - from . kernels import getIntegrableKernel - newKernel = getIntegrableKernel(kernel=self.kernelType, - dim=self.dim, - horizon=deepcopy(self.horizon), - interaction=None, - scaling=scaling, - phi=phi, - piecewise=self.piecewise, - boundary=True) - setREAL(newKernel.c_kernel_params, fEXPONENTINVERSE, getREAL(self.c_kernel_params, fEXPONENTINVERSE)) - if isinstance(newKernel.scaling, parametrizedTwoPointFunction): - assert newKernel.getParamPtrAddr() == newKernel.scaling.getParamPtrAddr() - if isinstance(self.scaling, parametrizedTwoPointFunction): - assert self.getParamPtrAddr() == self.scaling.getParamPtrAddr() - return newKernel - - def __dealloc__(self): - PyMem_Free(self.c_kernel_params) - - -cdef class ComplexKernel(ComplextwoPointFunction): - """A kernel functions that can be used to define a nonlocal operator.""" - - def __init__(self, INDEX_t dim, kernelType kType, function horizon, interactionDomain interaction, twoPointFunction scaling, twoPointFunction phi, BOOL_t piecewise=True, BOOL_t boundary=False, INDEX_t valueSize=1, REAL_t max_horizon=np.nan, **kwargs): - cdef: - parametrizedTwoPointFunction parametrizedScaling - int i - - self.dim = dim - self.valueSize = valueSize - self.kernelType = kType - self.piecewise = piecewise - self.boundary = boundary - - self.c_kernel_params = PyMem_Malloc(NUM_KERNEL_PARAMS*OFFSET) - for i in range(NUM_KERNEL_PARAMS): - ((self.c_kernel_params+i*OFFSET))[0] = NULL - setINDEX(self.c_kernel_params, fKDIM, dim) - - symmetric = isinstance(horizon, constant) and scaling.symmetric and interaction.symmetric and (phi is None or phi.symmetric) - super(ComplexKernel, self).__init__(symmetric, valueSize) - - if self.kernelType == GREENS_2D: - greensLambda = kwargs.get('greens2D_lambda', np.nan) - setREAL(self.c_kernel_params, fGREENS_LAMBDA, -greensLambda.imag) - self.min_singularity = 0. - self.max_singularity = 0. - self.singularityValue = 0. - elif self.kernelType == GREENS_3D: - greensLambda = kwargs.get('greens3D_lambda', np.nan) - setCOMPLEX(self.c_kernel_params, fGREENS_LAMBDA, greensLambda) - self.min_singularity = -1. - self.max_singularity = -1. - self.singularityValue = -1. - - self.horizon = horizon - self.variableHorizon = not isinstance(self.horizon, constant) - if self.variableHorizon: - self.horizonValue2 = np.nan - self.max_horizon = max_horizon - self.finiteHorizon = True - ((self.c_kernel_params+fHORIZONFUN))[0] = horizon - else: - self.horizonValue = self.horizon.value - self.max_horizon = self.horizon.value - self.finiteHorizon = self.horizon.value != np.inf - if self.kernelType == GAUSSIAN: - setREAL(self.c_kernel_params, fEXPONENTINVERSE, 1.0/(self.horizonValue/3.)**2) - - self.interaction = interaction - self.complement = self.interaction.complement - ((self.c_kernel_params+fINTERACTION))[0] = self.interaction - self.interaction.setParams(self.c_kernel_params) - - self.scalingPrePhi = scaling - self.phi = phi - if phi is not None: - scaling = phi*scaling - self.scaling = scaling - self.variableScaling = not isinstance(self.scaling, (constantFractionalLaplacianScaling, constantTwoPoint)) - if self.variableScaling: - if isinstance(self.scaling, parametrizedTwoPointFunction): - parametrizedScaling = self.scaling - parametrizedScaling.setParams(self.c_kernel_params) - self.scalingValue = np.nan - ((self.c_kernel_params+fSCALINGFUN))[0] = self.scaling - else: - self.scalingValue = self.scaling.value - - self.variable = self.variableHorizon or self.variableScaling - - if self.piecewise: - if not self.boundary: - if dim == 2: - if self.kernelType == GREENS_2D: - self.kernelFun = greens2Dcomplex - elif dim == 3: - if self.kernelType == GREENS_3D: - self.kernelFun = greens3Dcomplex - else: - raise NotImplementedError() - else: - raise NotImplementedError() - else: - self.kernelFun = updateAndEvalIntegrableComplex - - if not self.boundary: - if dim == 2: - if self.kernelType == GREENS_2D: - setComplexFun(self.c_kernel_params, fEVAL, greens2Dcomplex) - elif dim == 2: - if self.kernelType == GREENS_3D: - setComplexFun(self.c_kernel_params, fEVAL, greens3Dcomplex) - else: - raise NotImplementedError() - else: - raise NotImplementedError() - - @property - def singularityValue(self): - "The order of the singularity." - return getREAL(self.c_kernel_params, fSINGULARITY) - - @singularityValue.setter - def singularityValue(self, REAL_t singularity): - setREAL(self.c_kernel_params, fSINGULARITY, singularity) - - cdef REAL_t getSingularityValue(self): - return getREAL(self.c_kernel_params, fSINGULARITY) - - cdef void setSingularityValue(self, REAL_t singularity): - setREAL(self.c_kernel_params, fSINGULARITY, singularity) - - @property - def horizonValue(self): - "The value of the interaction horizon." - return sqrt(getREAL(self.c_kernel_params, fHORIZON2)) - - @horizonValue.setter - def horizonValue(self, REAL_t horizon): - setREAL(self.c_kernel_params, fHORIZON2, horizon**2) - - cdef REAL_t getHorizonValue(self): - return sqrt(getREAL(self.c_kernel_params, fHORIZON2)) - - cdef void setHorizonValue(self, REAL_t horizon): - setREAL(self.c_kernel_params, fHORIZON2, horizon**2) - - @property - def horizonValue2(self): - return getREAL(self.c_kernel_params, fHORIZON2) - - cdef REAL_t getHorizonValue2(self): - return getREAL(self.c_kernel_params, fHORIZON2) - - @horizonValue2.setter - def horizonValue2(self, REAL_t horizon2): - setREAL(self.c_kernel_params, fHORIZON2, horizon2) - - @property - def scalingValue(self): - "The value of the scaling factor." - return getREAL(self.c_kernel_params, fSCALING) - - @scalingValue.setter - def scalingValue(self, REAL_t scaling): - setREAL(self.c_kernel_params, fSCALING, scaling) - - cdef REAL_t getScalingValue(self): - return getREAL(self.c_kernel_params, fSCALING) - - cdef void setScalingValue(self, REAL_t scaling): - setREAL(self.c_kernel_params, fSCALING, scaling) - - cdef void evalParamsOnSimplices(self, REAL_t[::1] center1, REAL_t[::1] center2, REAL_t[:, ::1] simplex1, REAL_t[:, ::1] simplex2): - # Set the horizon. - if self.variableHorizon: - self.horizonValue = self.horizon.eval(center1) - - cdef void evalParams(self, REAL_t[::1] x, REAL_t[::1] y): - cdef: - REAL_t scalingValue - if self.piecewise: - if self.variableHorizon: - self.setHorizonValue(self.horizon.eval(x)) - if self.kernelType == GAUSSIAN: - setREAL(self.c_kernel_params, fEXPONENTINVERSE, 1.0/(self.horizonValue/3.)**2) - if self.variableScaling: - self.scaling.evalPtr(x.shape[0], &x[0], &y[0], &scalingValue) - self.scalingValue = scalingValue - - def evalParams_py(self, REAL_t[::1] x, REAL_t[::1] y): - "Evaluate the kernel parameters." - cdef: - REAL_t scalingValue - if self.piecewise: - self.evalParams(x, y) - else: - if self.variableHorizon: - self.setHorizonValue(self.horizon.eval(x)) - if self.kernelType == GAUSSIAN: - setREAL(self.c_kernel_params, fEXPONENTINVERSE, 1.0/(self.horizonValue/3.)**2) - if self.variableScaling: - self.scaling.evalPtr(x.shape[0], &x[0], &y[0], &scalingValue) - self.scalingValue = scalingValue - - cdef void evalParamsPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y): - cdef: - REAL_t[::1] xA - REAL_t scalingValue - if self.piecewise: - if self.variableHorizon: - xA = x - self.setHorizonValue(self.horizon.eval(xA)) - if self.kernelType == GAUSSIAN: - setREAL(self.c_kernel_params, fEXPONENTINVERSE, 1.0/(self.horizonValue/3.)**2) - if self.variableScaling: - self.scaling.evalPtr(dim, x, y, &scalingValue) - self.scalingValue = scalingValue - - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, COMPLEX_t[::1] value): - value[0] = self.kernelFun(&x[0], &y[0], self.c_kernel_params) - - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, COMPLEX_t* value): - value[0] = self.kernelFun(x, y, self.c_kernel_params) - - def __call__(self, REAL_t[::1] x, REAL_t[::1] y, BOOL_t callEvalParams=True): - "Evaluate the kernel." - if self.piecewise and callEvalParams: - self.evalParams(x, y) - return self.kernelFun(&x[0], &y[0], self.c_kernel_params) - - def eval_py(self, REAL_t[::1] x, REAL_t[::1] y, COMPLEX_t[::1] vec, BOOL_t callEvalParams=True): - "Evaluate the kernel." - if self.piecewise and callEvalParams: - self.evalParams(x, y) - self.eval(x, y, vec) - - def getModifiedKernel(self, - function horizon=None, - twoPointFunction scaling=None): - cdef: - Kernel newKernel - if horizon is None: - horizon = self.horizon - interaction = self.interaction - else: - if scaling is None and isinstance(self.scaling, (variableFractionalLaplacianScaling, - variableIntegrableScaling)): - scaling = self.scaling.getScalingWithDifferentHorizon() - interaction = type(self.interaction)() - if scaling is None: - scaling = self.scaling - from . kernels import getKernel - newKernel = getKernel(dim=self.dim, kernel=self.kernelType, horizon=horizon, interaction=interaction, scaling=scaling, piecewise=self.piecewise) - setREAL(newKernel.c_kernel_params, fEXPONENTINVERSE, getREAL(self.c_kernel_params, fEXPONENTINVERSE)) - if isinstance(newKernel.scaling, parametrizedTwoPointFunction): - assert newKernel.getParamPtrAddr() == newKernel.scaling.getParamPtrAddr() - if isinstance(self.scaling, parametrizedTwoPointFunction): - assert self.getParamPtrAddr() == self.scaling.getParamPtrAddr() - return newKernel - - def getComplementKernel(self): - "Get the complement kernel." - raise NotImplementedError() - from . kernels import getKernel - newKernel = getKernel(dim=self.dim, kernel=self.kernelType, horizon=self.horizon, interaction=self.interaction.getComplement(), scaling=self.scaling, piecewise=self.piecewise) - return newKernel - - def __repr__(self): - if self.kernelType == GREENS_2D: - kernelName = 'greens2D' - if self.kernelType == GREENS_3D: - kernelName = 'greens3D' - else: - raise NotImplementedError() - return "{}({}{}, {}, {})".format(self.__class__.__name__, kernelName, '' if not self.boundary else '-boundary', repr(self.interaction), self.scaling) - - def __reduce__(self): - return Kernel, (self.dim, self.kernelType, self.horizon, self.interaction, self.scalingPrePhi, self.phi, self.piecewise, self.boundary, self.singularityValue) - - def plot(self, x0=None): - "Plot the kernel function." - from matplotlib import ticker - import matplotlib.pyplot as plt - if x0 is None: - x0 = np.zeros((self.dim), dtype=REAL) - self.evalParams(x0, x0) - if self.finiteHorizon: - delta = self.horizonValue - else: - delta = 2. - x = np.linspace(-1.1*delta, 1.1*delta, 201) - if self.dim == 1: - vals = np.zeros_like(x) - for i in range(x.shape[0]): - y = x0+np.array([x[i]], dtype=REAL) - if np.linalg.norm(x0-y) > 1e-9 or self.singularityValue >= 0: - vals[i] = self(x0, y) - else: - vals[i] = np.nan - plt.plot(x, vals) - plt.yscale('log') - if not self.finiteHorizon: - plt.xlim([x[0], x[x.shape[0]-1]]) - if self.singularityValue < 0: - plt.ylim(top=np.nanmax(vals)) - plt.xlabel('$x-y$') - elif self.dim == 2: - X, Y = np.meshgrid(x, x) - Z = np.zeros_like(X) - for i in range(x.shape[0]): - for j in range(x.shape[0]): - y = x0+np.array([x[i], x[j]], dtype=REAL) - if np.linalg.norm(x0-y) > 1e-9 or self.singularityValue >= 0: - Z[i, j] = self(x0, y) - else: - Z[i, j] = np.nan - levels = np.logspace(np.log10(Z[np.absolute(Z)>0].min()), - np.log10(Z[np.absolute(Z)>0].max()), 10) - if levels[0] < levels[levels.shape[0]-1]: - plt.contourf(X, Y, Z, locator=ticker.LogLocator(), - levels=levels) - else: - plt.contourf(X, Y, Z) - plt.axis('equal') - plt.colorbar() - plt.xlabel('$x_1-y_1$') - plt.ylabel('$x_2-y_2$') - - def getBoundaryKernel(self): - "Get the boundary kernel. This is the kernel that corresponds to the elimination of a subdomain via Gauss theorem." - cdef: - Kernel newKernel - scaling = deepcopy(self.scaling) - if self.phi is not None: - phi = deepcopy(self.phi) - else: - phi = None - - from . kernels import getIntegrableKernel - newKernel = getIntegrableKernel(kernel=self.kernelType, - dim=self.dim, - horizon=deepcopy(self.horizon), - interaction=None, - scaling=scaling, - phi=phi, - piecewise=self.piecewise, - boundary=True) - setREAL(newKernel.c_kernel_params, fEXPONENTINVERSE, getREAL(self.c_kernel_params, fEXPONENTINVERSE)) - if isinstance(newKernel.scaling, parametrizedTwoPointFunction): - assert newKernel.getParamPtrAddr() == newKernel.scaling.getParamPtrAddr() - if isinstance(self.scaling, parametrizedTwoPointFunction): - assert self.getParamPtrAddr() == self.scaling.getParamPtrAddr() - return newKernel - - def __dealloc__(self): - PyMem_Free(self.c_kernel_params) - - -cdef class FractionalKernel(Kernel): - """A kernel function that can be used to define a fractional operator.""" - - def __init__(self, - INDEX_t dim, - fractionalOrderBase s, - function horizon, - interactionDomain interaction, - twoPointFunction scaling, - twoPointFunction phi=None, - BOOL_t piecewise=True, - BOOL_t boundary=False, - INDEX_t derivative=0, - REAL_t tempered=0., - REAL_t max_horizon=np.nan, - BOOL_t manifold=False): - if derivative == 0: - valueSize = 1 - elif derivative == 1: - valueSize = s.numParameters - elif derivative == 2: - valueSize = s.numParameters**2 - self.tempVec = uninitialized((s.numParameters), dtype=REAL) - else: - valueSize = 1 - - self.manifold = manifold - if not manifold: - kernelType = FRACTIONAL - else: - kernelType = MANIFOLD_FRACTIONAL - super(FractionalKernel, self).__init__(dim, kernelType, horizon, interaction, scaling, phi, piecewise, boundary, valueSize, max_horizon) - - self.symmetric = s.symmetric and isinstance(horizon, constant) and scaling.symmetric and interaction.symmetric and (phi is None or phi.symmetric) - self.derivative = derivative - self.temperedValue = tempered - - self.s = s - self.variableOrder = isinstance(self.s, variableFractionalOrder) - self.variableSingularity = self.variableOrder - if self.variableOrder: - self.sValue = np.nan - ((self.c_kernel_params+fORDERFUN))[0] = s - self.singularityValue = np.nan - if not self.boundary: - if not manifold: - self.min_singularity = -self.dim-2*self.s.min - self.max_singularity = -self.dim-2*self.s.max - else: - self.min_singularity = 1-self.dim-2*self.s.min - self.max_singularity = 1-self.dim-2*self.s.max - else: - if not manifold: - self.min_singularity = 1.-self.dim-2*self.s.min - self.max_singularity = 1.-self.dim-2*self.s.max - else: - self.min_singularity = 2.-self.dim-2*self.s.min - self.max_singularity = 2.-self.dim-2*self.s.max - else: - self.sValue = self.s.value - if not self.boundary: - if not manifold: - self.singularityValue = -self.dim-2*self.sValue - else: - self.singularityValue = 1-self.dim-2*self.sValue - else: - if not manifold: - self.singularityValue = 1.-self.dim-2*self.sValue - else: - self.singularityValue = 2.-self.dim-2*self.sValue - self.min_singularity = self.singularityValue - self.max_singularity = self.singularityValue - - self.variable = self.variableOrder or self.variableHorizon or self.variableScaling - - if self.piecewise: - if isinstance(self.horizon, constant) and self.horizon.value == np.inf: - if not self.boundary: - if not manifold: - if tempered == 0.: - if dim == 1: - self.kernelFun = fracKernelInfinite1D - elif dim == 2: - self.kernelFun = fracKernelInfinite2D - elif dim == 3: - self.kernelFun = fracKernelInfinite3D - else: - raise NotImplementedError() - else: - if dim == 1: - self.kernelFun = temperedFracKernelInfinite1D - elif dim == 2: - self.kernelFun = temperedFracKernelInfinite2D - elif dim == 3: - self.kernelFun = temperedFracKernelInfinite3D - else: - raise NotImplementedError() - else: - raise NotImplementedError() - else: - if not manifold: - if tempered == 0.: - if dim == 1: - self.kernelFun = fracKernelInfinite1Dboundary - elif dim == 2: - self.kernelFun = fracKernelInfinite2Dboundary - elif dim == 3: - self.kernelFun = fracKernelInfinite3Dboundary - else: - raise NotImplementedError() - else: - if dim == 1: - self.kernelFun = temperedFracKernelInfinite1Dboundary - elif dim == 2: - self.kernelFun = temperedFracKernelInfinite2Dboundary - elif dim == 3: - self.kernelFun = temperedFracKernelInfinite3Dboundary - else: - raise NotImplementedError() - # else: - # raise NotImplementedError() - else: - if not self.boundary: - if not manifold: - if tempered == 0.: - if dim == 1: - self.kernelFun = fracKernelFinite1D - elif dim == 2: - self.kernelFun = fracKernelFinite2D - elif dim == 3: - self.kernelFun = fracKernelFinite3D - else: - raise NotImplementedError() - else: - raise NotImplementedError() - else: - raise NotImplementedError() - else: - if not manifold: - if tempered == 0.: - if dim == 1: - self.kernelFun = fracKernelFinite1Dboundary - elif dim == 2: - self.kernelFun = fracKernelFinite2Dboundary - elif dim == 3: - self.kernelFun = fracKernelFinite3Dboundary - else: - raise NotImplementedError() - else: - raise NotImplementedError() - else: - raise NotImplementedError() - else: - self.kernelFun = updateAndEvalFractional - - if isinstance(self.horizon, constant) and self.horizon.value == np.inf: - if not self.boundary: - if not manifold: - if tempered == 0.: - if dim == 1: - setFun(self.c_kernel_params, fEVAL, fracKernelInfinite1D) - elif dim == 2: - setFun(self.c_kernel_params, fEVAL, fracKernelInfinite2D) - elif dim == 3: - setFun(self.c_kernel_params, fEVAL, fracKernelInfinite3D) - else: - raise NotImplementedError() - else: - if dim == 1: - setFun(self.c_kernel_params, fEVAL, temperedFracKernelInfinite1D) - elif dim == 2: - setFun(self.c_kernel_params, fEVAL, temperedFracKernelInfinite2D) - elif dim == 3: - setFun(self.c_kernel_params, fEVAL, temperedFracKernelInfinite3D) - else: - raise NotImplementedError() - else: - raise NotImplementedError() - else: - if not manifold: - if tempered == 0.: - if dim == 1: - setFun(self.c_kernel_params, fEVAL, fracKernelInfinite1Dboundary) - elif dim == 2: - setFun(self.c_kernel_params, fEVAL, fracKernelInfinite2Dboundary) - elif dim == 3: - setFun(self.c_kernel_params, fEVAL, fracKernelInfinite3Dboundary) - else: - raise NotImplementedError() - else: - if dim == 1: - setFun(self.c_kernel_params, fEVAL, temperedFracKernelInfinite1Dboundary) - elif dim == 2: - setFun(self.c_kernel_params, fEVAL, temperedFracKernelInfinite2Dboundary) - elif dim == 3: - setFun(self.c_kernel_params, fEVAL, temperedFracKernelInfinite3Dboundary) - else: - raise NotImplementedError() - # else: - # raise NotImplementedError() - else: - if not self.boundary: - if not manifold: - if tempered == 0.: - if dim == 1: - setFun(self.c_kernel_params, fEVAL, fracKernelFinite1D) - elif dim == 2: - setFun(self.c_kernel_params, fEVAL, fracKernelFinite2D) - elif dim == 3: - setFun(self.c_kernel_params, fEVAL, fracKernelFinite3D) - else: - raise NotImplementedError() - else: - raise NotImplementedError() - else: - raise NotImplementedError() - else: - if not manifold: - if tempered == 0.: - if dim == 1: - setFun(self.c_kernel_params, fEVAL, fracKernelFinite1Dboundary) - elif dim == 2: - setFun(self.c_kernel_params, fEVAL, fracKernelFinite2Dboundary) - elif dim == 3: - setFun(self.c_kernel_params, fEVAL, fracKernelFinite3Dboundary) - else: - raise NotImplementedError() - else: - raise NotImplementedError() - else: - raise NotImplementedError() - - @property - def sValue(self): - "The value of the fractional order" - return getREAL(self.c_kernel_params, fS) - - @sValue.setter - def sValue(self, REAL_t s): - setREAL(self.c_kernel_params, fS, s) - - cdef REAL_t getsValue(self): - return getREAL(self.c_kernel_params, fS) - - cdef void setsValue(self, REAL_t s): - setREAL(self.c_kernel_params, fS, s) - - @property - def temperedValue(self): - "The value of the tempering parameter" - return getREAL(self.c_kernel_params, fTEMPERED) - - @temperedValue.setter - def temperedValue(self, REAL_t tempered): - setREAL(self.c_kernel_params, fTEMPERED, tempered) - - cdef REAL_t gettemperedValue(self): - return getREAL(self.c_kernel_params, fTEMPERED) - - cdef void settemperedValue(self, REAL_t tempered): - setREAL(self.c_kernel_params, fTEMPERED, tempered) - - cdef void evalParamsOnSimplices(self, REAL_t[::1] center1, REAL_t[::1] center2, REAL_t[:, ::1] simplex1, REAL_t[:, ::1] simplex2): - # Set the max singularity and the horizon. - cdef: - REAL_t sValue, sValue2 - if self.variableOrder: - if self.s.symmetric: - self.s.evalPtr(center1.shape[0], ¢er1[0], ¢er2[0], &sValue) - else: - sValue = 0. - self.s.evalPtr(center1.shape[0], ¢er1[0], ¢er2[0], &sValue2) - sValue = max(sValue, sValue2) - self.s.evalPtr(center1.shape[0], ¢er2[0], ¢er1[0], &sValue2) - sValue = max(sValue, sValue2) - for i in range(simplex1.shape[0]): - self.s.evalPtr(center1.shape[0], &simplex1[i, 0], ¢er2[0], &sValue2) - sValue = max(sValue, sValue2) - for i in range(simplex2.shape[0]): - self.s.evalPtr(center1.shape[0], &simplex2[i, 0], ¢er1[0], &sValue2) - sValue = max(sValue, sValue2) - if not self.boundary: - self.setSingularityValue(-self.dim-2*sValue) - else: - self.setSingularityValue(1-self.dim-2*sValue) - if self.variableHorizon: - self.setHorizonValue(self.horizon.eval(center1)) - - cdef void evalParams(self, REAL_t[::1] x, REAL_t[::1] y): - cdef: - REAL_t sValue, scalingValue - if self.piecewise: - if self.variableOrder: - self.s.evalPtr(x.shape[0], &x[0], &y[0], &sValue) - if not self.boundary: - self.setSingularityValue(-self.dim-2*sValue) - else: - self.setSingularityValue(1-self.dim-2*sValue) - self.setsValue(sValue) - if self.variableHorizon: - self.setHorizonValue(self.horizon.eval(x)) - if self.variableScaling: - self.scaling.evalPtr(x.shape[0], &x[0], &y[0], &scalingValue) - self.setScalingValue(scalingValue) - - cdef void evalParamsPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y): - cdef: - REAL_t[::1] xA - REAL_t sValue, scalingValue - if self.piecewise: - if self.variableOrder: - self.s.evalPtr(dim, x, y, &sValue) - if not self.boundary: - self.setSingularityValue(-self.dim-2*sValue) - else: - self.setSingularityValue(1-self.dim-2*sValue) - self.setsValue(sValue) - if self.variableHorizon: - xA = x - self.setHorizonValue(self.horizon.eval(xA)) - if self.variableScaling: - self.scaling.evalPtr(dim, x, y, &scalingValue) - self.setScalingValue(scalingValue) - - def evalParams_py(self, REAL_t[::1] x, REAL_t[::1] y): - cdef: - REAL_t sValue, scalingValue - if self.piecewise: - self.evalParams(x, y) - else: - if self.variableOrder: - self.s.evalPtr(x.shape[0], &x[0], &y[0], &sValue) - self.setsValue(sValue) - if not self.boundary: - self.setSingularityValue(-self.dim-2*sValue) - else: - self.setSingularityValue(1-self.dim-2*sValue) - if self.variableHorizon: - self.setHorizonValue(self.horizon.eval(x)) - if self.variableScaling: - self.scaling.evalPtr(x.shape[0], &x[0], &y[0], & scalingValue) - self.setScalingValue(scalingValue) - - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] vec): - cdef: - INDEX_t i, j, k - REAL_t fac - if self.derivative == 0: - vec[0] = self.kernelFun(&x[0], &y[0], self.c_kernel_params) - elif self.derivative == 1: - fac = self.kernelFun(&x[0], &y[0], self.c_kernel_params) - self.s.evalGrad(x, y, vec) - for i in range(self.valueSize): - vec[i] *= fac - elif self.derivative == 2: - fac = self.kernelFun(&x[0], &y[0], self.c_kernel_params) - self.s.evalGrad(x, y, self.tempVec) - k = 0 - for i in range(self.s.numParameters): - for j in range(self.s.numParameters): - vec[k] = fac*self.tempVec[i]*self.tempVec[j] - k += 1 - - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* vec): - cdef: - INDEX_t i, j, k - REAL_t fac - if self.derivative == 0: - vec[0] = self.kernelFun(x, y, self.c_kernel_params) - elif self.derivative == 1: - fac = self.kernelFun(x, y, self.c_kernel_params) - self.s.evalGradPtr(dim, x, y, self.valueSize, vec) - for i in range(self.valueSize): - vec[i] *= fac - elif self.derivative == 2: - fac = self.kernelFun(&x[0], &y[0], self.c_kernel_params) - self.s.evalGradPtr(dim, x, y, self.s.numParameters, &self.tempVec[0]) - k = 0 - for i in range(self.s.numParameters): - for j in range(self.s.numParameters): - vec[k] = fac*self.tempVec[i]*self.tempVec[j] - k += 1 - - def getModifiedKernel(self, - fractionalOrderBase s=None, - function horizon=None, - twoPointFunction scaling=None): - if s is None: - s = self.s - else: - if scaling is None and isinstance(self.scaling, variableFractionalLaplacianScaling): - raise NotImplementedError() - if horizon is None: - horizon = self.horizon - interaction = self.interaction - else: - if scaling is None and isinstance(self.scaling, variableFractionalLaplacianScaling): - scaling = self.scaling.getScalingWithDifferentHorizon() - elif scaling is None and isinstance(self.scaling, productParametrizedTwoPoint): - if isinstance(self.scaling.f2, variableFractionalLaplacianScaling): - scaling = self.scaling.f1*self.scaling.f2.getScalingWithDifferentHorizon() - elif isinstance(self.scaling.f1, variableFractionalLaplacianScaling): - scaling = self.scaling.f2.getScalingWithDifferentHorizon()*self.scaling.f2 - else: - raise NotImplementedError() - interaction = deepcopy(self.interaction) - if scaling is None: - scaling = self.scaling - from . kernels import getFractionalKernel - newKernel = getFractionalKernel(dim=self.dim, s=s, horizon=horizon, interaction=interaction, scaling=scaling, piecewise=self.piecewise, boundary=self.boundary, derivative=self.derivative, manifold=self.manifold) - if isinstance(newKernel.scaling, parametrizedTwoPointFunction): - assert newKernel.getParamPtrAddr() == newKernel.scaling.getParamPtrAddr() - if isinstance(newKernel.interaction, parametrizedTwoPointFunction): - assert newKernel.getParamPtrAddr() == newKernel.interaction.getParamPtrAddr() - if isinstance(self.scaling, parametrizedTwoPointFunction): - assert self.getParamPtrAddr() == self.scaling.getParamPtrAddr() - return newKernel - - def getBoundaryKernel(self): - "Get the boundary kernel. This is the kernel that corresponds to the elimination of a subdomain via Gauss theorem." - cdef: - constantFractionalLaplacianScalingDerivative scal - variableFractionalLaplacianScalingWithDifferentHorizon scalVarDiffHorizon - variableFractionalLaplacianScaling scalVar - - s = deepcopy(self.s) - if not self.variableOrder: - fac = constantTwoPoint(1/s.value) - else: - fac = inverseTwoPoint(s) - phi = fac - - if isinstance(self.scaling, constantFractionalLaplacianScalingDerivative): - scal = self.scaling - scaling = constantFractionalLaplacianScalingDerivative(scal.dim, scal.s, scal.horizon, scal.normalized, True, scal.derivative, scal.tempered) - elif isinstance(self.scaling, variableFractionalLaplacianScalingWithDifferentHorizon): - scalVarDiffHorizon = self.scaling - scaling = variableFractionalLaplacianScalingWithDifferentHorizon(scalVarDiffHorizon.symmetric, scalVarDiffHorizon.normalized, True, scalVarDiffHorizon.derivative, scalVarDiffHorizon.horizonFun) - elif isinstance(self.scaling, variableFractionalLaplacianScaling): - scalVar = self.scaling - scaling = variableFractionalLaplacianScaling(scalVar.symmetric, scalVar.normalized, True, scalVar.derivative) - else: - scaling = deepcopy(self.scaling) - - from . kernels import getFractionalKernel - newKernel = getFractionalKernel(dim=self.dim, - s=s, - horizon=deepcopy(self.horizon), - interaction=None, - scaling=scaling, - phi=phi, - piecewise=self.piecewise, - boundary=True, - derivative=self.derivative, - manifold=self.manifold) - if isinstance(newKernel.scaling, parametrizedTwoPointFunction): - assert newKernel.getParamPtrAddr() == newKernel.scaling.getParamPtrAddr(), (newKernel.getParamPtrAddr(), newKernel.scaling.getParamPtrAddr()) - if isinstance(newKernel.interaction, parametrizedTwoPointFunction): - assert newKernel.getParamPtrAddr() == newKernel.interaction.getParamPtrAddr(), (newKernel.getParamPtrAddr(), newKernel.interaction.getParamPtrAddr()) - if isinstance(self.scaling, parametrizedTwoPointFunction): - assert self.getParamPtrAddr() == self.scaling.getParamPtrAddr(), (self.getParamPtrAddr(), self.scaling.getParamPtrAddr()) - if isinstance(self.interaction, parametrizedTwoPointFunction): - assert self.getParamPtrAddr() == self.interaction.getParamPtrAddr(), (self.getParamPtrAddr(), self.interaction.getParamPtrAddr()) - return newKernel - - def getComplementKernel(self): - from . kernels import getFractionalKernel - newKernel = getFractionalKernel(dim=self.dim, s=self.s, horizon=self.horizon, interaction=self.interaction.getComplement(), scaling=self.scaling, piecewise=self.piecewise) - return newKernel - - def getDerivativeKernel(self): - cdef: - constantFractionalLaplacianScaling scal - variableFractionalLaplacianScaling scalVar - interactionDomain interaction = deepcopy(self.interaction) - if isinstance(self.scaling, constantFractionalLaplacianScaling): - scal = self.scaling - scaling = constantFractionalLaplacianScalingDerivative(scal.dim, scal.s, scal.horizon, True, False, 1, scal.tempered) - elif isinstance(self.scaling, variableFractionalLaplacianScaling): - scalVar = self.scaling - scaling = variableFractionalLaplacianScaling(scalVar.symmetric, scalVar.normalized, scalVar.boundary, 1) - elif isinstance(self.scaling, constantTwoPoint): - scaling = constantFractionalLaplacianScalingDerivative(self.dim, self.sValue, np.nan, False, self.boundary, 1, 0.) - else: - raise NotImplementedError() - derivativeKernel = FractionalKernel(self.dim, self.s, self.horizon, interaction, scaling, self.phi, False, self.boundary, 1, self.temperedValue) - if isinstance(derivativeKernel.scaling, parametrizedTwoPointFunction): - assert derivativeKernel.getParamPtrAddr() == derivativeKernel.scaling.getParamPtrAddr(), (derivativeKernel.getParamPtrAddr(), derivativeKernel.scaling.getParamPtrAddr()) - if isinstance(derivativeKernel.interaction, parametrizedTwoPointFunction): - assert derivativeKernel.getParamPtrAddr() == derivativeKernel.interaction.getParamPtrAddr(), (derivativeKernel.getParamPtrAddr(), derivativeKernel.interaction.getParamPtrAddr()) - if isinstance(self.scaling, parametrizedTwoPointFunction): - assert self.getParamPtrAddr() == self.scaling.getParamPtrAddr(), (self.getParamPtrAddr(), self.scaling.getParamPtrAddr()) - if isinstance(self.interaction, parametrizedTwoPointFunction): - assert self.getParamPtrAddr() == self.interaction.getParamPtrAddr(), (self.getParamPtrAddr(), self.interaction.getParamPtrAddr()) - return derivativeKernel - - def getHessianKernel(self): - cdef: - constantFractionalLaplacianScaling scal - variableFractionalLaplacianScaling scalVar - interactionDomain interaction = deepcopy(self.interaction) - if isinstance(self.scaling, constantFractionalLaplacianScaling): - scal = self.scaling - scaling = constantFractionalLaplacianScalingDerivative(scal.dim, scal.s, scal.horizon, True, False, 2, scal.tempered) - elif isinstance(self.scaling, constantTwoPoint): - scaling = constantFractionalLaplacianScalingDerivative(self.dim, self.sValue, np.nan, False, self.boundary, 2, 0.) - elif isinstance(self.scaling, variableFractionalLaplacianScaling): - scalVar = self.scaling - scaling = variableFractionalLaplacianScaling(scalVar.symmetric, scalVar.normalized, scalVar.boundary, 2) - else: - raise NotImplementedError() - hessianKernel = FractionalKernel(self.dim, self.s, self.horizon, interaction, scaling, self.phi, False, self.boundary, 2, self.temperedValue) - if isinstance(hessianKernel.scaling, parametrizedTwoPointFunction): - assert hessianKernel.getParamPtrAddr() == hessianKernel.scaling.getParamPtrAddr(), (hessianKernel.getParamPtrAddr(), hessianKernel.scaling.getParamPtrAddr()) - if isinstance(hessianKernel.interaction, parametrizedTwoPointFunction): - assert hessianKernel.getParamPtrAddr() == hessianKernel.interaction.getParamPtrAddr(), (hessianKernel.getParamPtrAddr(), hessianKernel.interaction.getParamPtrAddr()) - if isinstance(self.scaling, parametrizedTwoPointFunction): - assert self.getParamPtrAddr() == self.scaling.getParamPtrAddr(), (self.getParamPtrAddr(), self.scaling.getParamPtrAddr()) - if isinstance(self.interaction, parametrizedTwoPointFunction): - assert self.getParamPtrAddr() == self.interaction.getParamPtrAddr(), (self.getParamPtrAddr(), self.interaction.getParamPtrAddr()) - return hessianKernel - - def __repr__(self): - if self.temperedValue != 0.: - return "kernel(tempered-fractional{}, {}, {}, {}, {})".format('' if not self.boundary else '-boundary', self.s, repr(self.interaction), self.scaling, self.temperedValue) - else: - return "kernel(fractional{}, {}, {}, {})".format('' if not self.boundary else '-boundary', self.s, repr(self.interaction), self.scaling) - - def __reduce__(self): - return FractionalKernel, (self.dim, self.s, self.horizon, self.interaction, self.scalingPrePhi, self.phi, self.piecewise, self.boundary, self.derivative, self.temperedValue, self.max_horizon, self.manifold) - - -cdef class RangedFractionalKernel(FractionalKernel): - def __init__(self, - INDEX_t dim, - admissibleOrders, - function horizon, - BOOL_t normalized=True, - REAL_t tempered=0., - REAL_t errorBound=-1., - INDEX_t M_min=1, INDEX_t M_max=20, - REAL_t xi=0.): - self.dim = dim - assert admissibleOrders.numParams == 1, "Cannot handle {} params".format(admissibleOrders.numParams) - self.admissibleOrders = admissibleOrders - assert isinstance(horizon, constant) - self.horizon = horizon - if isinstance(horizon, constant) and horizon.value == np.inf: - self.interaction = fullSpace() - else: - self.interaction = ball2_retriangulation() - self.normalized = normalized - self.tempered = tempered - - self.setOrder(admissibleOrders.getLowerBounds()[0]) - - self.errorBound = errorBound - self.M_min = M_min - self.M_max = M_max - self.xi = xi - - def setOrder(self, REAL_t s): - assert self.admissibleOrders.isAdmissible(s) - sFun = constFractionalOrder(s) - dim = self.dim - horizon = self.horizon - interactionDomain = self.interaction - tempered = self.tempered - if self.normalized: - scaling = constantFractionalLaplacianScaling(dim, sFun.value, horizon.value, tempered=tempered) - else: - scaling = constantTwoPoint(0.5) - super(RangedFractionalKernel, self).__init__(dim, sFun, horizon, interactionDomain, scaling, tempered=tempered) - - def getFrozenKernel(self, REAL_t s): - assert self.admissibleOrders.isAdmissible(s) - sFun = constFractionalOrder(s) - dim = self.dim - horizon = self.horizon - tempered = self.tempered - interactionDomain = deepcopy(self.interaction) - if self.normalized: - scaling = constantFractionalLaplacianScaling(dim, sFun.value, horizon.value, tempered=tempered) - else: - scaling = constantTwoPoint(0.5) - return FractionalKernel(dim, sFun, horizon, interactionDomain, scaling) - - def __repr__(self): - return 'ranged '+super(RangedFractionalKernel, self).__repr__() - - def __reduce__(self): - return RangedFractionalKernel, (self.dim, self.admissibleOrders, self.horizon, self.normalized, self.tempered, self.errorBound, self.M_min, self.M_max, self.xi) - - -cdef class RangedVariableFractionalKernel(FractionalKernel): - def __init__(self, - INDEX_t dim, - function blockIndicator, - admissibleOrders, - function horizon, - BOOL_t normalized=True, - REAL_t errorBound=-1., - INDEX_t M_min=1, INDEX_t M_max=20, - REAL_t xi=0.): - self.dim = dim - self.blockIndicator = blockIndicator - self.admissibleOrders = admissibleOrders - - assert isinstance(horizon, constant) - self.horizon = horizon - if isinstance(horizon, constant) and horizon.value == np.inf: - self.interaction = fullSpace() - else: - self.interaction = ball2_retriangulation() - self.normalized = normalized - - numBlocks = np.around(sqrt(admissibleOrders.numParams)) - assert numBlocks*numBlocks == admissibleOrders.numParams - self.setOrder(admissibleOrders.getLowerBounds().reshape((numBlocks, numBlocks))) - - self.errorBound = errorBound - self.M_min = M_min - self.M_max = M_max - self.xi = xi - - def setOrder(self, REAL_t[:, ::1] sVals): - assert self.admissibleOrders.isAdmissible(np.array(sVals, copy=False).flatten()) - sFun = piecewiseConstantFractionalOrder(self.dim, self.blockIndicator, sVals) - dim = self.dim - horizon = self.horizon - interactionDomain = self.interaction - if self.normalized: - scaling = variableFractionalLaplacianScaling(sFun.symmetric) - else: - scaling = constantTwoPoint(0.5) - super(RangedVariableFractionalKernel, self).__init__(dim, sFun, horizon, interactionDomain, scaling) - - def getFrozenKernel(self, REAL_t[:, ::1] sVals): - assert self.admissibleOrders.isAdmissible(np.array(sVals, copy=False).flatten()) - sFun = piecewiseConstantFractionalOrder(self.dim, self.blockIndicator, sVals) - dim = self.dim - horizon = self.horizon - interactionDomain = self.interaction - if self.normalized: - scaling = variableFractionalLaplacianScaling(sFun.symmetric) - else: - scaling = constantTwoPoint(0.5) - return FractionalKernel(dim, sFun, horizon, interactionDomain, scaling) - - def __repr__(self): - return 'ranged '+super(RangedVariableFractionalKernel, self).__repr__() diff --git a/nl/PyNucleus_nl/kernels_decl_{SCALAR}.pxi b/nl/PyNucleus_nl/kernels_decl_{SCALAR}.pxi new file mode 100644 index 00000000..85c1d824 --- /dev/null +++ b/nl/PyNucleus_nl/kernels_decl_{SCALAR}.pxi @@ -0,0 +1,67 @@ +################################################################################### +# Copyright 2021 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. # +# If you want to use this code, please refer to the README.rst and LICENSE files. # +################################################################################### + +ctypedef {SCALAR}_t (*{SCALAR_label_lc_}kernel_fun_t)(REAL_t *x, REAL_t *y, void* user_data) + + +cdef class {SCALAR_label}Kernel({SCALAR_label}twoPointFunction): + cdef: + public INDEX_t dim + public INDEX_t manifold_dim + public kernelType kernelType + public REAL_t min_singularity + public REAL_t max_singularity + public REAL_t min_log_singularity + public REAL_t max_log_singularity + public REAL_t max_horizon + public function horizon + public interactionDomain interaction + public twoPointFunction scalingPrePhi + public twoPointFunction scaling + public twoPointFunction phi + public BOOL_t variableSingularity + public BOOL_t variableHorizon + public BOOL_t finiteHorizon + public BOOL_t complement + public BOOL_t variableScaling + public BOOL_t variable + public BOOL_t piecewise + {SCALAR_label_lc_}kernel_fun_t kernelFun + void *c_kernel_params + REAL_t[::1] n + cdef setKernelFun(self, {SCALAR_label_lc_}kernel_fun_t kernelFun) + cdef BOOL_t getBoundary(self) + cdef void setBoundary(self, BOOL_t boundary) + cdef REAL_t getSingularityValue(self) + cdef void setSingularityValue(self, REAL_t log_singularity) + cdef REAL_t getLogSingularityValue(self) + cdef void setLogSingularityValue(self, REAL_t log_singularity) + cdef REAL_t getHorizonValue(self) + cdef void setHorizonValue(self, REAL_t horizon) + cdef REAL_t getHorizonValue2(self) + cdef REAL_t getScalingValue(self) + cdef void setScalingValue(self, REAL_t scaling) + cdef void evalParamsOnSimplicesPtr(self, INDEX_t dim, REAL_t* center1, REAL_t* center2, REAL_t* simplex1, REAL_t* simplex2) + cdef void updateParams(self, INDEX_t dim, REAL_t* x, REAL_t* y) + cdef void evalParamsPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y) + cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, {SCALAR}_t* value) + cdef void setSimplices(self, REAL_t[:, ::1] simplex1, REAL_t[:, ::1] simplex2) + + +cdef class {SCALAR_label}BoundaryKernel({SCALAR_label}Kernel): + cdef: + public BOOL_t isSphericalManifold + + +cdef class {SCALAR_label}ErrorKernel({SCALAR_label}Kernel): + pass + + +cdef class {SCALAR_label}MultiSingularityKernel({SCALAR_label}twoPointFunction): + cdef: + public list kernels + {SCALAR}_t[::1] tempVec diff --git a/nl/PyNucleus_nl/kernels_{SCALAR}.pxi b/nl/PyNucleus_nl/kernels_{SCALAR}.pxi new file mode 100644 index 00000000..909aaeef --- /dev/null +++ b/nl/PyNucleus_nl/kernels_{SCALAR}.pxi @@ -0,0 +1,1036 @@ +################################################################################### +# Copyright 2021 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. # +# If you want to use this code, please refer to the README.rst and LICENSE files. # +################################################################################### + +cdef class {SCALAR_label}Kernel({SCALAR_label}twoPointFunction): + """A kernel functions that can be used to define a nonlocal operator.""" + + def __init__(self, INDEX_t dim, kernelType kType, function horizon, interactionDomain interaction, twoPointFunction scaling, twoPointFunction phi, BOOL_t piecewise=True, BOOL_t boundary=False, INDEX_t valueSize=1, REAL_t max_horizon=np.nan, INDEX_t manifold_dim=-1, **kwargs): + cdef: + parametrizedTwoPointFunction parametrizedScaling + int i + + self.c_kernel_params = PyMem_Malloc(NUM_KERNEL_PARAMS*OFFSET) + for i in range(NUM_KERNEL_PARAMS): + ((self.c_kernel_params+i*OFFSET))[0] = NULL + + self.dim = dim + if manifold_dim == -1: + self.manifold_dim = dim + else: + self.manifold_dim = manifold_dim + assert valueSize >= 1, "Creation of kernel with valueSize = {}".format(valueSize) + self.valueSize = valueSize + self.kernelType = kType + self.piecewise = piecewise + self.boundary = boundary + + setINDEX(self.c_kernel_params, fKDIM, dim) + + symmetric = scaling.symmetric and interaction.symmetric and (phi is None or phi.symmetric) + super({SCALAR_label}Kernel, self).__init__(symmetric, valueSize) + + self.min_log_singularity = 0. + self.max_log_singularity = 0. + self.logSingularityValue = 0. + + if self.kernelType == INDICATOR: + self.min_singularity = self.boundary + self.max_singularity = self.boundary + self.singularityValue = self.boundary + setREAL(self.c_kernel_params, fEXPONENT, self.boundary) + elif self.kernelType in (GAUSSIAN, EXPONENTIAL, POLYNOMIAL, LOGINVERSEDISTANCE): + self.min_singularity = 0. + self.max_singularity = 0. + self.singularityValue = 0. + elif self.kernelType == PERIDYNAMIC: + setREAL(self.c_kernel_params, fEXPONENT, self.boundary-1.) + self.min_singularity = self.boundary-1. + self.max_singularity = self.boundary-1. + self.singularityValue = self.boundary-1. + elif self.kernelType == MONOMIAL: + monomialPower = kwargs.get('monomialPower', np.nan) + setREAL(self.c_kernel_params, fEXPONENT, self.boundary+monomialPower) + self.min_singularity = self.boundary+self.manifold+monomialPower + self.max_singularity = self.boundary+self.manifold+monomialPower + self.singularityValue = self.boundary+self.manifold+monomialPower + elif self.kernelType == GREENS_2D: + greensLambda = kwargs.get('greens_lambda', np.nan) + setREAL(self.c_kernel_params, fGREENS_LAMBDA, -greensLambda.imag) + self.min_singularity = 0. + self.max_singularity = 0. + self.singularityValue = 0. + elif self.kernelType == GREENS_3D: + greensLambda = kwargs.get('greens_lambda', np.nan) + setCOMPLEX(self.c_kernel_params, fGREENS_LAMBDA, greensLambda) + self.min_singularity = -1. + self.max_singularity = -1. + self.singularityValue = -1. + + self.horizon = horizon + self.variableHorizon = not isinstance(self.horizon, constant) + if self.variableHorizon: + ((self.c_kernel_params+fHORIZONFUN))[0] = horizon + self.horizonValue2 = np.nan + self.finiteHorizon = True + self.max_horizon = max_horizon + else: + self.horizonValue = self.horizon.value + self.max_horizon = self.horizon.value + self.finiteHorizon = self.horizon.value != np.inf + + if self.kernelType == GAUSSIAN: + if self.finiteHorizon: + setREAL(self.c_kernel_params, fEXPONENTINVERSE, 1.0/(self.horizonValue/3.)**2) + else: + variance = kwargs.get('variance', 1.0) + setREAL(self.c_kernel_params, fEXPONENTINVERSE, 0.5/variance**self.dim) + elif self.kernelType == EXPONENTIAL: + exponentialRate = kwargs.get('exponentialRate', 1.0) + setREAL(self.c_kernel_params, fTEMPERED, exponentialRate) + elif self.kernelType == POLYNOMIAL: + a = kwargs.get('a', 1.0) + setREAL(self.c_kernel_params, fEXPONENTINVERSE, a) + + self.interaction = interaction + self.complement = self.interaction.complement + ((self.c_kernel_params+fINTERACTION))[0] = self.interaction + self.interaction.setParams(self.c_kernel_params) + + self.phi = phi + self.scalingPrePhi = scaling + if phi is not None: + scaling = phi*scaling + self.scaling = scaling + self.variableScaling = not isinstance(self.scaling, constantTwoPoint) + if self.variableScaling: + ((self.c_kernel_params+fSCALINGFUN))[0] = self.scaling + if isinstance(self.scaling, parametrizedTwoPointFunction): + parametrizedScaling = self.scaling + parametrizedScaling.setParams(self.c_kernel_params) + self.scalingValue = np.nan + else: + self.scalingValue = self.scaling.value + + self.variableSingularity = False + + self.variable = self.variableHorizon or self.variableScaling or self.variableSingularity + + IF {IS_REAL}: + if self.kernelType in (MONOMIAL, INDICATOR, PERIDYNAMIC): + self.setKernelFun(monomialKernel) + elif self.kernelType == GAUSSIAN: + self.setKernelFun(gaussianKernel) + elif self.kernelType == EXPONENTIAL: + self.setKernelFun(temperedMonomialKernel) + elif self.kernelType == LOGINVERSEDISTANCE: + self.setKernelFun(logInverseDistanceKernel) + elif self.kernelType == POLYNOMIAL: + if dim == 1: + self.setKernelFun(polynomialKernel) + else: + raise NotImplementedError() + IF {IS_COMPLEX}: + if self.kernelType == GREENS_2D: + if dim == 2: + self.setKernelFun(greens2Dcomplex) + else: + raise NotImplementedError() + elif self.kernelType == GREENS_3D: + if dim == 3: + self.setKernelFun(greens3Dcomplex) + else: + raise NotImplementedError() + + @staticmethod + def build(INDEX_t dim, + kernel, + horizon, + twoPointFunction scaling=None, + interaction=None, + BOOL_t normalized=True, + BOOL_t piecewise=True, + twoPointFunction phi=None, + REAL_t monomialPower=np.nan, + REAL_t variance=1., + REAL_t exponentialRate=1.0, + REAL_t exponentInverse=np.nan, + REAL_t a=1., + REAL_t max_horizon=np.nan, + INDEX_t termNo=-1, + INDEX_t manifold_dim=-1): + dim_ = _getDim(dim) + kType = _getKernelType(kernel) + horizonFun = _getHorizon(horizon) + interaction = _getInteraction(interaction, horizonFun) + + if scaling is None: + if normalized: + if isinstance(horizonFun, constant): + scaling = constantIntegrableScaling(kType, interaction, dim_, horizonFun.value, gaussian_variance=variance, exponentialRate=exponentialRate) + else: + scaling = variableIntegrableScaling(kType, interaction) + else: + scaling = constantTwoPoint(1.0) + if (not scaling.symmetric) or (phi is not None and not phi.symmetric): + piecewise = False + return Kernel(dim_, kType=kType, horizon=horizonFun, interaction=interaction, scaling=scaling, phi=phi, piecewise=piecewise, + boundary=False, monomialPower=monomialPower, max_horizon=max_horizon, variance=variance, exponentialRate=exponentialRate, a=a, exponentInverse=exponentInverse, + manifold_dim=manifold_dim) + + cdef void setSimplices(self, REAL_t[:, ::1] simplex1, REAL_t[:, ::1] simplex2): + pass + + def setNormal(self, REAL_t[::1] normal): + assert self.n is not None and self.n.shape[0] == normal.shape[0] + assign(self.n, normal) + + cdef setKernelFun(self, {SCALAR_label_lc_}kernel_fun_t kernelFun): + self.kernelFun = kernelFun + + def getParamPtrAddr(self): + return self.c_kernel_params + + @property + def is_integrable(self): + dim = self.dim-self.boundary-self.manifold + if dim > 0: + return (dim+self.singularityValue > 0.) + else: + if self.kernelType == LOGINVERSEDISTANCE: + return False + else: + return (self.singularityValue >= 0.) + + @property + def manifold(self): + return False + + @property + def boundary(self): + "The order of the boundary." + return getBOOL(self.c_kernel_params, fBOUNDARY) + + @boundary.setter + def boundary(self, BOOL_t boundary): + setBOOL(self.c_kernel_params, fBOUNDARY, boundary) + + cdef BOOL_t getBoundary(self): + return getBOOL(self.c_kernel_params, fBOUNDARY) + + cdef void setBoundary(self, BOOL_t boundary): + setBOOL(self.c_kernel_params, fBOUNDARY, boundary) + + @property + def singularityValue(self): + "The order of the singularity." + return getREAL(self.c_kernel_params, fSINGULARITY) + + @singularityValue.setter + def singularityValue(self, REAL_t singularity): + setREAL(self.c_kernel_params, fSINGULARITY, singularity) + + cdef REAL_t getSingularityValue(self): + return getREAL(self.c_kernel_params, fSINGULARITY) + + cdef void setSingularityValue(self, REAL_t singularity): + setREAL(self.c_kernel_params, fSINGULARITY, singularity) + + @property + def logSingularityValue(self): + "The order of the logarithmic singularity." + return getREAL(self.c_kernel_params, fLOG_SINGULARITY) + + @logSingularityValue.setter + def logSingularityValue(self, REAL_t log_singularity): + setREAL(self.c_kernel_params, fLOG_SINGULARITY, log_singularity) + + cdef REAL_t getLogSingularityValue(self): + return getREAL(self.c_kernel_params, fLOG_SINGULARITY) + + cdef void setLogSingularityValue(self, REAL_t log_singularity): + setREAL(self.c_kernel_params, fLOG_SINGULARITY, log_singularity) + + @property + def horizonValue(self): + "The value of the interaction horizon." + return sqrt(getREAL(self.c_kernel_params, fHORIZON2)) + + @horizonValue.setter + def horizonValue(self, REAL_t horizon): + setREAL(self.c_kernel_params, fHORIZON2, horizon**2) + + cdef REAL_t getHorizonValue(self): + return sqrt(getREAL(self.c_kernel_params, fHORIZON2)) + + cdef void setHorizonValue(self, REAL_t horizon): + setREAL(self.c_kernel_params, fHORIZON2, horizon**2) + + @property + def horizonValue2(self): + return getREAL(self.c_kernel_params, fHORIZON2) + + cdef REAL_t getHorizonValue2(self): + return getREAL(self.c_kernel_params, fHORIZON2) + + @horizonValue2.setter + def horizonValue2(self, REAL_t horizon2): + setREAL(self.c_kernel_params, fHORIZON2, horizon2) + + @property + def scalingValue(self): + "The value of the scaling factor." + return getREAL(self.c_kernel_params, fSCALING) + + @scalingValue.setter + def scalingValue(self, REAL_t scaling): + setREAL(self.c_kernel_params, fSCALING, scaling) + + cdef REAL_t getScalingValue(self): + return getREAL(self.c_kernel_params, fSCALING) + + cdef void setScalingValue(self, REAL_t scaling): + setREAL(self.c_kernel_params, fSCALING, scaling) + + def getLongDescription(self): + cdef: + str descr = '' + dict params = {} + params['d'] = self.dim + if self.finiteHorizon: + params['\\delta'] = self.horizon + if self.kernelType == INDICATOR: + descr = '' + elif self.kernelType == PERIDYNAMIC: + descr = '\\frac{1}{|x-y|}' + elif self.kernelType == FRACTIONAL: + descr = '\\frac{1}{|x-y|^{d+2s}}' + elif self.kernelType == MANIFOLD_FRACTIONAL: + descr = '(\\zeta(d+2s, d(x, y)) + \\zeta(d+2s, 1-d(x, y)))' + elif self.kernelType == GAUSSIAN: + if self.finiteHorizon: + exponentInverse = self.getKernelParam('exponentInverse') + params['a'] = exponentInverse + descr = '\\exp(-a |x-y|^2)' + else: + variance = self.getKernelParam('variance') + params['\\sigma'] = variance + descr = '\\exp(-0.5 |x-y|^2/\\sigma^{d})' + elif self.kernelType == EXPONENTIAL: + exponentialRate = self.getKernelParam('exponentialRate') + params['a'] = exponentialRate + descr = '\\exp(-a |x-y|)' + elif self.kernelType == POLYNOMIAL: + a = self.getKernelParam('a') + params['a'] = a + descr = '\\frac{a^3 |x-y|^2}{(a^2 + |x-y|^2)^2}' + paramsStr = ', '.join([k+'='+str(v) for k, v in params.items()]) + return self.scaling.getLongDescription() + ' ' + descr + ' ' + self.interaction.getLongDescription() + ', ' + paramsStr + + def getKernelParams(self): + params = [] + if self.kernelType == GAUSSIAN: + if self.finiteHorizon: + params.append('exponentInverse') + else: + params.append('variance') + elif self.kernelType == EXPONENTIAL: + params.append('exponentialRate') + elif self.kernelType == POLYNOMIAL: + params.append('a') + elif self.kernelType == MONOMIAL: + params.append('monomialPower') + return params + + def getKernelParam(self, str param): + if self.kernelType == GAUSSIAN: + if self.finiteHorizon: + if param == 'exponentInverse': + return getREAL(self.c_kernel_params, fEXPONENTINVERSE) + else: + if param == 'variance': + exponentInverse = getREAL(self.c_kernel_params, fEXPONENTINVERSE) + variance = 1.0/(2.0*exponentInverse)**(1.0/self.dim) + return variance + elif self.kernelType == EXPONENTIAL: + if param == 'exponentialRate': + return getREAL(self.c_kernel_params, fTEMPERED) + elif self.kernelType == POLYNOMIAL: + if param == 'a': + return getREAL(self.c_kernel_params, fEXPONENTINVERSE) + elif self.kernelType == MONOMIAL: + if param == 'monomialPower': + return getREAL(self.c_kernel_params, fEXPONENT)-self.boundary + raise NotImplementedError("Parameter not available: {}".format(param)) + + def getKernelKwargs(self): + kwargs = {} + for kernelParam in self.getKernelParams(): + kwargs[kernelParam] = self.getKernelParam(kernelParam) + return kwargs + + cdef void evalParamsOnSimplicesPtr(self, INDEX_t dim, REAL_t* center1, REAL_t* center2, REAL_t* simplex1, REAL_t* simplex2): + cdef: + REAL_t[::1] center1A + # Set the horizon. + if self.variableHorizon: + center1A = center1 + self.horizonValue = self.horizon.eval(center1A) + + def evalParams_py(self, REAL_t[::1] x, REAL_t[::1] y): + "Evaluate the kernel parameters." + cdef: + REAL_t scalingValue + self.evalParamsPtr(x.shape[0], &x[0], &y[0]) + + cdef void updateParams(self, INDEX_t dim, REAL_t* x, REAL_t* y): + cdef: + REAL_t[::1] xA + REAL_t scalingValue + if self.variableHorizon: + xA = x + self.horizonValue = self.horizon.eval(xA) + if self.kernelType == GAUSSIAN: + setREAL(self.c_kernel_params, fEXPONENTINVERSE, 1.0/(self.horizonValue/3.)**2) + elif self.kernelType == POLYNOMIAL: + setREAL(self.c_kernel_params, fEXPONENTINVERSE, 1.0/(self.horizonValue/3.)**2) + if self.variableScaling: + self.scaling.evalPtr(dim, x, y, &scalingValue) + self.scalingValue = scalingValue + + cdef void evalParamsPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y): + cdef: + REAL_t[::1] xA + REAL_t scalingValue + if self.piecewise: + self.updateParams(dim, x, y) + + cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, {SCALAR}_t* vec): + cdef: + REAL_t[::1] xA + REAL_t scalingValue + if not self.piecewise: + self.updateParams(dim, x, y) + vec[0] = self.kernelFun(x, y, self.c_kernel_params) + + def __call__(self, REAL_t[::1] x, REAL_t[::1] y, BOOL_t callEvalParams=True): + "Evaluate the kernel." + cdef: + np.ndarray[REAL_t, ndim = 1] vec + np.ndarray[COMPLEX_t, ndim = 1] vecComplex + + assert x.shape[0] == y.shape[0], (x.shape[0], y.shape[0]) + self.evalParamsPtr(x.shape[0], &x[0], &y[0]) + if {IS_REAL}: + vec = uninitialized((self.valueSize), dtype=REAL) + self.evalPtr(x.shape[0], &x[0], &y[0], &vec[0]) + if self.valueSize == 1: + return vec[0] + else: + return vec + else: + vecComplex = uninitialized((self.valueSize), dtype=COMPLEX) + self.evalPtr(x.shape[0], &x[0], &y[0], &vecComplex[0]) + if self.valueSize == 1: + return vecComplex[0] + else: + return vecComplex + + def eval_py(self, REAL_t[::1] x, REAL_t[::1] y, {SCALAR}_t[::1] vec, BOOL_t callEvalParams=True): + "Evaluate the kernel." + if self.piecewise and callEvalParams: + self.evalParamsPtr(x.shape[0], &x[0], &y[0]) + self.evalPtr(x.shape[0], &x[0], &y[0], &vec[0]) + + def _checkNewKernel(self, {SCALAR_label}Kernel newKernel): + # checks + if isinstance(newKernel.scaling, parametrizedTwoPointFunction): + assert newKernel.getParamPtrAddr() == newKernel.scaling.getParamPtrAddr() + if isinstance(self.scaling, parametrizedTwoPointFunction): + assert self.getParamPtrAddr() == self.scaling.getParamPtrAddr() + if isinstance(self.scaling, parametrizedTwoPointFunction) and isinstance(newKernel.scaling, parametrizedTwoPointFunction): + assert self.scaling.getParamPtrAddr() != newKernel.scaling.getParamPtrAddr() + + if isinstance(newKernel.interaction, parametrizedTwoPointFunction): + assert newKernel.getParamPtrAddr() == newKernel.interaction.getParamPtrAddr() + if isinstance(self.interaction, parametrizedTwoPointFunction): + assert self.interaction.getParamPtrAddr() == self.interaction.getParamPtrAddr() + if isinstance(self.interaction, parametrizedTwoPointFunction) and isinstance(newKernel.interaction, parametrizedTwoPointFunction): + assert self.interaction.getParamPtrAddr() != newKernel.interaction.getParamPtrAddr() + + def getModifiedKernel(self, + function horizon=None, + interactionDomain interaction=None, + twoPointFunction scaling=None, + twoPointFunction phi=None): + cdef: + Kernel newKernel + if interaction is None: + if horizon is None: + horizon = deepcopy(self.horizon) + interaction = deepcopy(self.interaction) + else: + if scaling is None and isinstance(self.scaling, (variableFractionalLaplacianScaling, + variableIntegrableScaling)): + scaling = self.scaling.getScalingWithDifferentHorizon() + if isinstance(horizon, constant) and horizon.value == np.inf: + interaction = fullSpace() + else: + interaction = deepcopy(self.interaction) + else: + assert horizon is None + if scaling is None and phi is None: + scaling = deepcopy(self.scalingPrePhi) + phi = deepcopy(self.phi) + if scaling is None: + scaling = deepcopy(self.scaling) + kwargs = self.getKernelKwargs() + if isinstance(self, {SCALAR_label}BoundaryKernel): + newKernel = {SCALAR_label}BoundaryKernel.build(dim=self.dim, kernel=self.kernelType, horizon=horizon, interaction=interaction, scaling=scaling, phi=phi, piecewise=self.piecewise, manifold_dim=self.manifold_dim, **kwargs) + else: + newKernel = {SCALAR_label}Kernel.build(dim=self.dim, kernel=self.kernelType, horizon=horizon, interaction=interaction, scaling=scaling, phi=phi, piecewise=self.piecewise, manifold_dim=self.manifold_dim, **kwargs) + setREAL(newKernel.c_kernel_params, fEXPONENTINVERSE, getREAL(self.c_kernel_params, fEXPONENTINVERSE)) + + self._checkNewKernel(newKernel) + return newKernel + + def getComplementKernel(self): + "Get the complement kernel." + raise NotImplementedError() + newKernel = {SCALAR_label}Kernel.build(dim=self.dim, kernel=self.kernelType, horizon=self.horizon, interaction=self.interaction.getComplement(), scaling=self.scaling, piecewise=self.piecewise) + self._checkNewKernel(newKernel) + return newKernel + + def __repr__(self): + extraInfo = {} + for p in self.getKernelParams(): + extraInfo[p] = self.getKernelParam(p) + if self.kernelType == INDICATOR: + kernelName = 'indicator' + elif self.kernelType == PERIDYNAMIC: + kernelName = 'peridynamic' + elif self.kernelType == GAUSSIAN: + kernelName = 'Gaussian' + elif self.kernelType == EXPONENTIAL: + kernelName = 'exponential' + elif self.kernelType == POLYNOMIAL: + kernelName = 'polynomial' + elif self.kernelType == LOGINVERSEDISTANCE: + kernelName = 'logInverseDistance' + elif self.kernelType == MONOMIAL: + kernelName = 'monomial' + elif self.kernelType == ERROR: + kernelName = 'ERROR' + else: + raise NotImplementedError(self.kernelType) + if len(extraInfo) > 0: + extraInfo = ', '.join([str(k)+'='+str(v) for k, v in extraInfo.items()]) + return "{}({}, {}, {}, {})".format(self.__class__.__name__, kernelName, repr(self.interaction), self.scaling, extraInfo) + else: + return "{}({}, {}, {})".format(self.__class__.__name__, kernelName, repr(self.interaction), self.scaling) + + def __reduce__(self): + kwargs = {} + kwargs = self.getKernelKwargs() + + def helper(args, kwargs): + return {SCALAR_label}Kernel(*args, **kwargs) + + return helper, ((self.dim, self.kernelType, self.horizon, self.interaction, self.scalingPrePhi, self.phi, self.piecewise, self.boundary, self.valueSize, self.max_horizon, self.manifold_dim), kwargs) + + def plot(self, REAL_t[::1] x0=None): + "Plot the kernel function." + from matplotlib import ticker + import matplotlib.pyplot as plt + if x0 is None: + x0 = np.zeros((self.dim), dtype=REAL) + self.evalParamsPtr(x0.shape[0], &x0[0], &x0[0]) + if self.finiteHorizon: + delta = self.horizonValue + else: + delta = 2. + x = np.linspace(-1.1*delta, 1.1*delta, 201) + if self.dim == 1: + vals = np.zeros((x.shape[0], self.valueSize)) + for i in range(x.shape[0]): + y = x0+np.array([x[i]], dtype=REAL) + if np.linalg.norm(x0-y) > 1e-9 or self.singularityValue >= 0: + vals[i, :] = self(x0, y) + else: + vals[i, :] = np.nan + for k in range(self.valueSize): + plt.plot(x, vals[:, k]) + plt.yscale('log') + if not self.finiteHorizon: + plt.xlim([x[0], x[x.shape[0]-1]]) + if self.singularityValue < 0: + plt.ylim(top=np.nanmax(vals)) + plt.xlabel('$x-y$') + elif self.dim == 2: + X, Y = np.meshgrid(x, x) + Z = np.zeros_like(X) + for i in range(x.shape[0]): + for j in range(x.shape[0]): + y = x0+np.array([x[i], x[j]], dtype=REAL) + if np.linalg.norm(x0-y) > 1e-9 or self.singularityValue >= 0: + Z[j, i] = self(x0, y) + else: + Z[j, i] = np.nan + levels = np.logspace(np.log10(Z[np.absolute(Z)>0].min()), + np.log10(Z[np.absolute(Z)>0].max()), 10) + if levels[0] < levels[levels.shape[0]-1]: + plt.contourf(X, Y, Z, locator=ticker.LogLocator(), + levels=levels) + else: + plt.contourf(X, Y, Z) + plt.axis('equal') + plt.colorbar() + plt.xlabel('$y_1-x_1$') + plt.ylabel('$y_2-x_2$') + + def _evalKernel(self, REAL_t[::1] evaluation_nodes): + """Evaluates the kernel at distances given by evaluation_nodes. + This assumes that the kernel only depends on the distance between x and y.""" + cdef: + REAL_t[::1] origin + REAL_t[::1] y + REAL_t node + INDEX_t k + origin = np.zeros((self.dim), dtype=REAL) + y = np.zeros((self.dim), dtype=REAL) + values = np.zeros((evaluation_nodes.shape[0]), dtype=REAL) + if self.manifold: + raise NotImplementedError(self.interaction) + else: + if self.boundary: + self.n[0] = 1. + for j in range(1, self.manifold_dim): + self.n[j] = 0. + for k, node in enumerate(evaluation_nodes): + y[0] = node + values[k] = self(origin, y) + assert np.all(np.isfinite(values[1:])), (np.array(evaluation_nodes), np.array(values)) + return values + + def _integrateKernel(self, REAL_t[::1] nodes): + """Obtain a discrete representation of the boundary kernel via quadrature.""" + from scipy.integrate import quad + + cdef: + INDEX_t k, numNodes, manifold_dim, dim + REAL_t[::1] values + REAL_t[::1] origin + + assert self.horizonValue == np.inf + assert nodes[0] == 0. + + dim = self.dim + manifold_dim = self.manifold_dim + numNodes = nodes.shape[0] + values = np.zeros((numNodes), dtype=REAL) + + # We compute + # values[i] = \int_{.}^{dist_i} gamma(t) (t/dist_i)^{manifold_dim-1} dt + # where . just means that we do not care about the shift constant. + # + # We compute + # \int_{.}^{dist_1} gamma(t) t^{manifold_dim-1} + # analytically and then obtain + # \int_{.}^{dist_j} gamma(t) t^{manifold_dim-1} = \int_{.}^{dist_1} gamma(t) t^{manifold_dim-1} + \sum_{k=2}^{j} \int_{dist_{k-1}}^{dist_k} gamma(t) t^{manifold_dim-1} + # using numerical quadrature for all other intervals. + + if self.manifold: + raise NotImplementedError() + else: + def pointAtDistance(t): + p = np.zeros((dim), dtype=REAL) + p[0] = t + return p + + origin = pointAtDistance(0.) + + def integrand(t): + return self(origin, pointAtDistance(t)) * t**(manifold_dim-1) + + # Assume that kernel is C*dist^singularity on [0, dist_1]. + self(origin, pointAtDistance(nodes[1])) + if self.singularityValue+manifold_dim != 0.: + values[1] = self.scalingValue * nodes[1]**(self.singularityValue+manifold_dim)/(self.singularityValue+manifold_dim) + else: + values[1] = self.scalingValue * log(nodes[1]) + + for k in range(2, numNodes): + values[k] = quad(integrand, nodes[k-1], nodes[k], points=[0.], epsabs=1e-10, limit=100)[0] + + values[0] = 0. + for k in range(1, numNodes): + values[k] += values[k-1] + + for k in range(numNodes): + if nodes[k] > 0: + values[k] *= nodes[k]**(1-manifold_dim) + + assert np.all(np.isfinite(values[1:])), np.array(values) + return values + + def getCorrectedKernel(self, REAL_t[::1] interpolation_nodes, {SCALAR}_t[::1] interpolation_values, str kind='linear'): + """Return a kernel of the form + + \\gamma(|x-y|) = I[k(|x-y|)/k_M(|x-y|)] k_M(|x-y|) + + where k_M is given by self and I[.] is the piecewise linear interpolation wrt interpolation_nodes. + The values of k are given by interpolation_values, meaning that + + \\gamma(interpolation_nodes[j]) = interpolation_values[j] + + """ + cdef: + {SCALAR}_t[::1] base_values, correction_factor + INDEX_t k, numNodes + numNodes = interpolation_nodes.shape[0] + assert numNodes == interpolation_values.shape[0] + assert np.min(interpolation_nodes) == 0., (np.min(interpolation_nodes), self.kernelType, self.singularityValue) + assert np.all(np.array(interpolation_nodes)[1:] > np.array(interpolation_nodes)[:numNodes-1]) + # interpolate base kernel at nodes + if self.valueSize == 1: + base_values = self._evalKernel(interpolation_nodes) + else: + base_values2d = self._evalKernel(interpolation_nodes) + base_values = np.ascontiguousarray(base_values2d[:, 0]) + if self.boundary: + for k in range(base_values.shape[0]): + base_values[k] = -base_values[k] + assert np.all(np.isfinite(base_values[1:])), (np.array(interpolation_nodes), np.array(base_values)) + correction_factor = uninitialized((numNodes), dtype={SCALAR}) + for k in range(numNodes): + if abs(base_values[k]) < 1e-15: + correction_factor[k] = 1.0 + elif k == 0 and not (np.isfinite(interpolation_values[k]) and np.isfinite(base_values[k])): + correction_factor[k] = interpolation_values[1]/base_values[1] + else: + correction_factor[k] = interpolation_values[k]/base_values[k] + assert np.all(np.isfinite(correction_factor)), (np.array(interpolation_nodes), np.array(interpolation_values), np.array(base_values), np.array(correction_factor)) + if kind == 'linear': + correction = UniformLookup1D(interpolation_nodes[0], interpolation_nodes[numNodes-1], correction_factor, outOfBoundsValue=np.nan) + else: + correction = Lookup1D(interpolation_nodes, correction_factor, outOfBoundsValue=np.nan, kind=kind) + interaction = deepcopy(self.interaction) + phi = functionOfDistance(interaction, correction) + newKernel = self.getModifiedKernel(interaction=interaction, phi=phi) + self._checkNewKernel(newKernel) + return newKernel + + def interpolate(self, REAL_t[::1] interpolation_nodes, str kind='linear'): + """Interpolate the kernel wrt interpolation_nodes. This can be useful if evaluation of the kernel is expensive.""" + interpolation_values = self._evalKernel(interpolation_nodes) + base_kernel = {SCALAR_label}Kernel.build(kernel=MONOMIAL, + monomialPower=self.singularityValue, + dim=self.dim, + manifold_dim=self.manifold_dim, + horizon=deepcopy(self.horizon), + scaling=constantTwoPoint(self.scalingValue), + interaction=deepcopy(self.interaction)) + newKernel = base_kernel.getCorrectedKernel(interpolation_nodes, interpolation_values, kind) + self._checkNewKernel(newKernel) + return newKernel + + @property + def isInterpolatedKernel(self): + return isinstance(self.phi, functionOfDistance) and isinstance(self.phi.fun, (UniformLookup1D, Lookup1D)) + + def getBoundaryKernel(self, REAL_t[::1] interpolation_nodes=None, str kind='linear'): + "Get the boundary kernel. This is the kernel that corresponds to the elimination of a subdomain via Gauss theorem." + cdef: + Kernel newKernel + assert not self.boundary + if interpolation_nodes is None and self.isInterpolatedKernel: + if isinstance(self.phi.fun, UniformLookup1D): + interpolation_nodes = np.linspace(self.phi.fun.a, self.phi.fun.b, self.phi.fun.vals.shape[0]) + kind = 'linear' + elif isinstance(self.phi.fun, Lookup1D): + interpolation_nodes = self.phi.fun.x + kind = self.phi.fun.kind + if interpolation_nodes is None: + scaling = deepcopy(self.scaling) + if isinstance(self.phi, constantTwoPoint): + phi = deepcopy(self.phi) + elif self.phi is not None: + newKernel = {SCALAR_label}BoundaryKernel.build(kernel=ERROR, + dim=self.dim, + horizon=deepcopy(self.horizon), + interaction=None, + scaling=scaling, + phi=deepcopy(self.phi), + piecewise=self.piecewise) + return newKernel + else: + phi = None + + if self.kernelType in (MONOMIAL, INDICATOR, PERIDYNAMIC): + if abs(self.singularityValue+self.dim) > 1e-9: + scaling *= constantTwoPoint(1.0/(self.singularityValue+self.dim)) + else: + # log distance kernel case + scaling *= constantTwoPoint(-1.0) + elif self.kernelType == EXPONENTIAL: + a = getREAL(self.c_kernel_params, fTEMPERED) + scaling *= constantTwoPoint(-1.0/a) + + kwargs = {} + if self.kernelType == MONOMIAL: + kwargs['monomialPower'] = self.getKernelParam('monomialPower') + newKernel = {SCALAR_label}BoundaryKernel.build(kernel=self.kernelType, + dim=self.dim, + horizon=deepcopy(self.horizon), + interaction=None, + scaling=scaling, + phi=phi, + piecewise=self.piecewise, + **kwargs) + setREAL(newKernel.c_kernel_params, fEXPONENTINVERSE, getREAL(self.c_kernel_params, fEXPONENTINVERSE)) + else: + if not self.variable or (self.variableScaling and isinstance(self.scalingPrePhi, constantTwoPoint) and isinstance(self.phi, functionOfDistance)): + interpolation_values = self._integrateKernel(interpolation_nodes) + base_boundary_kernel = BoundaryKernel.build(kernel=MONOMIAL, monomialPower=self.singularityValue, dim=self.dim, horizon=np.inf, normalized=False, interaction=deepcopy(self.interaction), manifold_dim=self.manifold_dim) + if abs(self.dim+1+base_boundary_kernel.singularityValue) < 1e-9: + base_boundary_kernel = BoundaryKernel.build(kernel=LOGINVERSEDISTANCE, dim=self.dim, horizon=np.inf, normalized=False, interaction=deepcopy(self.interaction), manifold_dim=self.manifold_dim) + newKernel = base_boundary_kernel.getCorrectedKernel(interpolation_nodes, interpolation_values, kind) + else: + newKernel = {SCALAR_label}BoundaryKernel.build(kernel=ERROR, + dim=self.dim, + horizon=deepcopy(self.horizon), + interaction=None, + scaling=deepcopy(self.scaling), + phi=deepcopy(self.phi), + piecewise=self.piecewise, + manifold_dim=self.manifold_dim) + + + self._checkNewKernel(newKernel) + return newKernel + + def __dealloc__(self): + PyMem_Free(self.c_kernel_params) + + +cdef class {SCALAR_label}BoundaryKernel({SCALAR_label}Kernel): + def __init__(self, INDEX_t dim, kernelType kType, function horizon, interactionDomain interaction, twoPointFunction scaling, twoPointFunction phi, BOOL_t piecewise=True, INDEX_t valueSize=1, REAL_t max_horizon=np.nan, INDEX_t manifold_dim=-1, **kwargs): + super({SCALAR_label}BoundaryKernel, self).__init__(dim=dim, + kType=kType, + horizon=horizon, + interaction=interaction, + scaling=scaling, + phi=phi, + piecewise=piecewise, + boundary=True, + valueSize=valueSize, + max_horizon=max_horizon, + manifold_dim=manifold_dim, + **kwargs) + IF {IS_REAL}: + if self.kernelType in (INDICATOR, MONOMIAL, PERIDYNAMIC): + if abs(dim-self.boundary+self.singularityValue) < 1e-9: + self.kernelType = LOGINVERSEDISTANCE + self.setKernelFun(logInverseDistanceKernel) + else: + self.setKernelFun(monomialKernel) + elif self.kernelType == GAUSSIAN: + self.setKernelFun(gaussianKernelBoundary) + elif self.kernelType == EXPONENTIAL: + self.setKernelFun(temperedMonomialKernel) + elif self.kernelType == POLYNOMIAL: + if dim == 1: + self.setKernelFun(polynomialKernel1Dboundary) + else: + raise NotImplementedError() + self.isSphericalManifold = False + self.n = uninitialized((self.manifold_dim), dtype=REAL) + + cdef void setSimplices(self, REAL_t[:, ::1] simplex1, REAL_t[:, ::1] simplex2): + cdef: + REAL_t val + if not self.isSphericalManifold: + if self.dim == 1: + if 0.5*(simplex1[0, 0]+simplex1[1, 0]) > simplex2[0, 0]: + self.n[0] = -1. + else: + self.n[0] = 1. + elif self.dim == 2: + # (x_1-x_0)^\perp + self.n[0] = simplex2[1, 1] - simplex2[0, 1] + self.n[1] = simplex2[0, 0] - simplex2[1, 0] + val = 1./sqrt(mydot(self.n, self.n)) + self.n[0] *= val + self.n[1] *= val + elif self.dim == 3: + self.n[0] = (simplex2[1, 1]-simplex2[0, 1])*(simplex2[2, 2]-simplex2[0, 2])-(simplex2[1, 2]-simplex2[0, 2])*(simplex2[2, 1]-simplex2[0, 1]) + self.n[1] = (simplex2[1, 2]-simplex2[0, 2])*(simplex2[2, 0]-simplex2[0, 0])-(simplex2[1, 0]-simplex2[0, 0])*(simplex2[2, 2]-simplex2[0, 2]) + self.n[2] = (simplex2[1, 0]-simplex2[0, 0])*(simplex2[2, 1]-simplex2[0, 1])-(simplex2[1, 1]-simplex2[0, 1])*(simplex2[2, 0]-simplex2[0, 0]) + val = 1./sqrt(mydot(self.n, self.n)) + self.n[0] *= val + self.n[1] *= val + self.n[2] *= val + else: + if self.manifold_dim == 1: + self.n[0] = 1. + + @staticmethod + def build(INDEX_t dim, + kernel, + horizon, + twoPointFunction scaling=None, + interaction=None, + BOOL_t normalized=True, + BOOL_t piecewise=True, + twoPointFunction phi=None, + REAL_t monomialPower=np.nan, + REAL_t variance=1., + REAL_t exponentialRate=1.0, + REAL_t exponentInverse=np.nan, + REAL_t a=1., + REAL_t max_horizon=np.nan, + INDEX_t manifold_dim=-1): + dim_ = _getDim(dim) + kType = _getKernelType(kernel) + horizonFun = _getHorizon(horizon) + interaction = _getInteraction(interaction, horizonFun) + + if scaling is None: + if normalized: + if isinstance(horizonFun, constant): + scaling = constantIntegrableScaling(kType, interaction, dim_, horizonFun.value, gaussian_variance=variance, exponentialRate=exponentialRate) + else: + scaling = variableIntegrableScaling(kType, interaction) + else: + scaling = constantTwoPoint(1.0) + if (not scaling.symmetric) or (phi is not None and not phi.symmetric): + piecewise = False + return BoundaryKernel(dim_, kType=kType, horizon=horizonFun, interaction=interaction, scaling=scaling, phi=phi, piecewise=piecewise, + monomialPower=monomialPower, max_horizon=max_horizon, variance=variance, exponentialRate=exponentialRate, a=a, exponentInverse=exponentInverse, + manifold_dim=manifold_dim) + + cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, {SCALAR}_t* vec): + cdef: + {SCALAR}_t temp + INDEX_t i + if not self.piecewise: + self.updateParams(dim, x, y) + temp = self.kernelFun(&x[0], &y[0], self.c_kernel_params) + if not self.isSphericalManifold: + if self.interaction.dist2 > 0: + temp /= sqrt(self.interaction.dist2) + vec[0] = 0. + for i in range(dim): + vec[0] += temp * (x[i]-y[i])*self.n[i] + else: + for i in range(self.dim): + vec[i] = temp + else: + if self.interaction.dist2 > 0: + vec[0] = temp + else: + for i in range(self.dim): + vec[i] = temp + + def getBoundaryKernel(self, REAL_t[::1] interpolation_nodes=None): + raise NotImplementedError() + + +cdef class {SCALAR_label}ErrorKernel({SCALAR_label}Kernel): + cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, {SCALAR}_t* vec): + raise Exception("Cannot evaluate {SCALAR_label}ErrorKernel") + + +cdef class {SCALAR_label}MultiSingularityKernel({SCALAR_label}twoPointFunction): + def __init__(self, list kernels): + cdef: + {SCALAR_label}Kernel kernel + assert len(kernels) >= 2 + symmetric = True + valueSize = kernels[0].valueSize + for kernel in kernels: + symmetric &= kernel.symmetric + assert kernel.valueSize == valueSize + super().__init__(symmetric, valueSize) + self.kernels = kernels + self.tempVec = uninitialized((self.valueSize), dtype={SCALAR}) + + @property + def is_integrable(self): + integrable = self.kernels[0].is_integrable + for kernel in self.kernels[1:]: + integrable &= kernel.is_integrable + return integrable + + @property + def horizon(self): + horizon = self.kernels[0].horizon + assert isinstance(horizon, constant) + hv = horizon.value + for kernel in self.kernels[1:]: + horizon = kernel.horizon + assert isinstance(horizon, constant) + hv = max(hv, horizon.value) + return constant(hv) + + @property + def singularityValue(self): + sv = self.kernels[0].singularityValue + for kernel in self.kernels[1:]: + sv = min(sv, kernel.singularityValue) + return sv + + @property + def max_horizon(self): + mh = self.kernels[0].max_horizon + for kernel in self.kernels[1:]: + mh = min(mh, kernel.max_horizon) + return mh + + cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, {SCALAR}_t* vec): + cdef: + INDEX_t i, k + {SCALAR_label}Kernel kernel + {SCALAR}_t fac + for i in range(self.valueSize): + vec[i] = 0. + for k in range(len(self.kernels)): + kernel = self.kernels[k] + kernel.evalParamsPtr(dim, x, y) + kernel.evalPtr(dim, x, y, &self.tempVec[0]) + fac = kernel.scaling.values[kernel.scaling.termNo] + for i in range(self.valueSize): + vec[i] += fac*self.tempVec[i] + + def setNormal(self, REAL_t[::1] normal): + for k in range(len(self.kernels)): + kernel = self.kernels[k] + kernel.setNormal(normal) + + def getModifiedKernel(self, + function horizon=None, + interactionDomain interaction=None, + twoPointFunction scaling=None, + twoPointFunction phi=None): + mkernels = [] + for kernel in self.kernels: + mkernels.append(kernel.getModifiedKernel(horizon=horizon, interaction=interaction, scaling=scaling, phi=phi)) + return type(self)(mkernels) + + def getBoundaryKernel(self, REAL_t[::1] interpolation_nodes=None): + bkernels = [] + for kernel in self.kernels: + bkernels.append(kernel.getBoundaryKernel(interpolation_nodes)) + return type(self)(bkernels) + + def __repr__(self): + return ' + '.join([repr(kernel) for kernel in self.kernels]) + + def getLongDescription(self): + return ' + '.join([k.getLongDescription() for k in self.kernels]) + + def __reduce__(self): + return {SCALAR_label}MultiSingularityKernel, (self.kernels, ) diff --git a/nl/PyNucleus_nl/nonlocalAssembly.pxd b/nl/PyNucleus_nl/nonlocalAssembly.pxd index c03978fd..5067aec1 100644 --- a/nl/PyNucleus_nl/nonlocalAssembly.pxd +++ b/nl/PyNucleus_nl/nonlocalAssembly.pxd @@ -12,13 +12,13 @@ from PyNucleus_fem.quadrature cimport (simplexQuadratureRule, quadQuadratureRule simplexDuffyTransformation, simplexXiaoGimbutas) from PyNucleus_fem.DoFMaps cimport DoFMap from . bitset cimport tupleDictMASK -from . clusterMethodCy cimport (tree_node, - farFieldClusterPair, - H2Matrix, - DistributedH2Matrix_globalData, - DistributedH2Matrix_localData, - DistributedLinearOperator, - VectorH2Matrix) +from . clusterMethod cimport (tree_node, + farFieldClusterPair, + H2Matrix, + DistributedH2Matrix_globalData, + DistributedH2Matrix_localData, + DistributedLinearOperator, + VectorH2Matrix) from . nonlocalOperator cimport (double_local_matrix_t, Complexdouble_local_matrix_t, nonlocalOperator, @@ -43,9 +43,11 @@ from PyNucleus_base.linear_operators cimport LinearOperator, ComplexLinearOperat from PyNucleus_base.linear_operators cimport VectorLinearOperator, ComplexVectorLinearOperator from PyNucleus_fem.meshCy cimport meshBase from PyNucleus_fem.DoFMaps cimport DoFMap -from . kernelsCy cimport (Kernel, - ComplexKernel, - FractionalKernel) +from . kernels cimport (Kernel, + ComplexKernel, + FractionalKernel, + ErrorKernel, + ComplexErrorKernel) include "nonlocalAssembly_decl_REAL.pxi" diff --git a/nl/PyNucleus_nl/nonlocalAssembly.pyx b/nl/PyNucleus_nl/nonlocalAssembly.pyx index 70a33f54..9b2cb898 100644 --- a/nl/PyNucleus_nl/nonlocalAssembly.pyx +++ b/nl/PyNucleus_nl/nonlocalAssembly.pyx @@ -8,7 +8,7 @@ import numpy as np cimport numpy as np -from libc.math cimport sin, cos, M_PI as pi +from libc.math cimport sin, cos, M_PI as pi, sqrt from libcpp.map cimport map from cpython.long cimport PyLong_FromSsize_t from PyNucleus_base.myTypes import INDEX, REAL, COMPLEX, BOOL @@ -17,7 +17,7 @@ from PyNucleus_base.intTuple cimport intTuple from PyNucleus_base.ip_norm cimport (ip_distributed_nonoverlapping, norm_distributed_nonoverlapping) from PyNucleus_fem.functions cimport function, constant -from PyNucleus_fem.DoFMaps cimport P0_DoFMap, P1_DoFMap, Product_DoFMap +from PyNucleus_fem.DoFMaps cimport P0_DoFMap, P1_DoFMap from PyNucleus_fem.DoFMaps import DoFMap2str from PyNucleus_fem.meshCy cimport sortEdge, encode_edge, decode_edge from PyNucleus_fem.femCy cimport local_matrix_t @@ -51,22 +51,22 @@ from . fractionalOrders cimport (fractionalOrderBase, piecewiseConstantFractionalOrder, variableFractionalOrder, singleVariableUnsymmetricFractionalOrder) -from . kernelNormalization cimport variableFractionalLaplacianScaling -from . kernels import getFractionalKernel -from . clusterMethodCy import (assembleFarFieldInteractions, - getDoFBoxesAndCells, - getFractionalOrders, - getAdmissibleClusters, - getCoveringClusters, - symmetrizeNearFieldClusters, - trimTree) -from . clusterMethodCy cimport (refinementType, - refinementParams, - GEOMETRIC, - MEDIAN, - BARYCENTER, - queryAdmissibility, - INADMISSIBLE) +from . kernelNormalization cimport (constantFractionalLaplacianScaling, + variableFractionalLaplacianScaling) +from . clusterMethod import (assembleFarFieldInteractions, + getDoFBoxesAndCells, + getFractionalOrders, + getAdmissibleClusters, + getCoveringClusters, + symmetrizeNearFieldClusters, + trimTree) +from . clusterMethod cimport (refinementType, + refinementParams, + GEOMETRIC, + MEDIAN, + BARYCENTER, + queryAdmissibility, + INADMISSIBLE) from copy import deepcopy import logging from logging import INFO @@ -76,6 +76,7 @@ mpi4py.rc.initialize = False from mpi4py import MPI from mpi4py cimport MPI include "panelTypes.pxi" +include "kernel_params.pxi" cdef REAL_t INTERFACE_DOF = np.inf @@ -136,6 +137,7 @@ cdef class horizonSurfaceIntegral(function): REAL_t horizon REAL_t[:, ::1] quadNodes REAL_t[::1] quadWeights, y + REAL_t[::1] val def __init__(self, Kernel kernel, REAL_t horizon): cdef: @@ -162,6 +164,8 @@ cdef class horizonSurfaceIntegral(function): else: raise NotImplementedError() self.y = uninitialized((self.kernel.dim), dtype=REAL) + self.val = uninitialized((self.kernel.valueSize), dtype=REAL) + assert self.kernel.valueSize == 1 cdef inline REAL_t eval(self, REAL_t[::1] x): cdef: @@ -172,10 +176,10 @@ cdef class horizonSurfaceIntegral(function): for k in range(self.quadNodes.shape[0]): for j in range(dim): self.y[j] = x[j]+self.quadNodes[k, j] - self.kernel.evalParams(x, self.y) + self.kernel.n[j] = -self.quadNodes[k, j]/self.horizon + self.kernel.evalParamsPtr(x.shape[0], &x[0], &self.y[0]) self.kernel.evalPtr(x.shape[0], &x[0], &self.y[0], &val) - # val = self.kernel.scalingValue*pow(self.horizon, 1-dim-2*s)/s - fac -= val * self.quadWeights[k] + fac += val * self.quadWeights[k] return fac @@ -199,7 +203,8 @@ cdef class horizonCorrected(TimeStepperLinearOperator): assert isinstance(kernel.horizon, constant) if Ainf is None: - scaling = constantTwoPoint(0.5) + # Get kernel with infinite horizon and no scaling + scaling = constantFractionalLaplacianScaling(dim=kernel.dim, s=kernel.sValue, horizon=1., normalized=False, boundary=False, derivative=0, tempered=0., termNo=0) infiniteKernel = kernel.getModifiedKernel(horizon=constant(np.inf), scaling=scaling) infBuilder = nonlocalBuilder(self.dm, infiniteKernel, zeroExterior=True, comm=self.comm, logging=self.logging) self.Ainf = infBuilder.getH2() @@ -242,7 +247,7 @@ cdef class horizonCorrected(TimeStepperLinearOperator): correction = builder.getH2() self.S = self.Ainf - self.facS = 2.*C + self.facS = C if isinstance(self.kernel.s, constFractionalOrder): if self.mesh.dim == 1: @@ -252,7 +257,7 @@ cdef class horizonCorrected(TimeStepperLinearOperator): else: raise NotImplementedError() s = self.kernel.sValue - self.M = -correction + (-vol*C*pow(horizon, 1-self.mesh.dim-2*s)/s) * self.mass + self.M = -correction + (vol*C*pow(horizon, 1-self.mesh.dim-2*s)/(-2.0*s)) * self.mass else: self.mass.setZero() @@ -314,7 +319,7 @@ cdef class horizonCorrected(TimeStepperLinearOperator): builder.local_matrix_surface.eval(contribZeroExterior, panel) iM.getDoFsElem(cellNo3) if builder.local_matrix_surface.symmetricLocalMatrix: - iM.addToMatrixElemSym(contribZeroExterior, -2*C*fac) + iM.addToMatrixElemSym(contribZeroExterior, -C*fac) else: raise NotImplementedError() @@ -338,7 +343,7 @@ cdef class horizonCorrected(TimeStepperLinearOperator): builder.local_matrix_surface.eval(contribZeroExterior, panel) iM.getDoFsElem(cellNo3) if builder.local_matrix_surface.symmetricLocalMatrix: - iM.addToMatrixElemSym(contribZeroExterior, -2*C*fac) + iM.addToMatrixElemSym(contribZeroExterior, -C*fac) else: raise NotImplementedError() @@ -367,7 +372,7 @@ def assembleNonlocalOperator(meshBase mesh, bint zeroExterior=True, MPI.Comm comm=None, **kwargs): - kernel = getFractionalKernel(mesh.dim, s, horizon) + kernel = FractionalKernel.build(dim=mesh.dim, s=s, horizon=horizon) builder = nonlocalBuilder(dm, kernel, params, zeroExterior, comm, **kwargs) return builder.getDense() @@ -497,7 +502,7 @@ def assembleNearField(list Pnear, bint zeroExterior=True, comm=None, **kwargs): - kernel = getFractionalKernel(mesh.dim, s, horizon) + kernel = FractionalKernel.build(dim=mesh.dim, s=s, horizon=horizon) builder = nonlocalBuilder(dm, kernel, params, zeroExterior, comm, logging=True, **kwargs) A = builder.assembleClusters(Pnear) return A diff --git a/nl/PyNucleus_nl/nonlocalAssembly_{SCALAR}.pxi b/nl/PyNucleus_nl/nonlocalAssembly_{SCALAR}.pxi index a00f8ad0..97830ada 100644 --- a/nl/PyNucleus_nl/nonlocalAssembly_{SCALAR}.pxi +++ b/nl/PyNucleus_nl/nonlocalAssembly_{SCALAR}.pxi @@ -912,7 +912,7 @@ cdef class {SCALAR_label}nonlocalBuilder: cdef: MASK_t mask - assert kernel.dim == self.dm.mesh.dim, "Kernel dimension must match dm.mesh dimension" + assert kernel.dim == self.dm.mesh.dim, "Kernel dimension must match dm.mesh dimension. kernel.dim = {} !+ {} = self.dm.mesh.dim".format(kernel.dim, self.dm.mesh.dim) self.kernel = kernel @@ -950,17 +950,20 @@ cdef class {SCALAR_label}nonlocalBuilder: # surface integrals + kernelInfHorizon = kernel.getModifiedKernel(horizon=constant(np.inf)) + kernelInfHorizonBoundary = kernelInfHorizon.getBoundaryKernel() + kernelBoundary = kernel.getBoundaryKernel() if self.local_matrix_zeroExterior is None: - self.local_matrix_zeroExterior = self.getLocalMatrixBoundaryZeroExterior(kernel.getModifiedKernel(horizon=constant(np.inf)).getBoundaryKernel(), self.params) - self.local_matrix_surface = self.getLocalMatrixBoundaryZeroExterior(kernel.getBoundaryKernel(), self.params) + self.local_matrix_zeroExterior = self.getLocalMatrixBoundaryZeroExterior(kernelInfHorizonBoundary, self.params) + self.local_matrix_surface = self.getLocalMatrixBoundaryZeroExterior(kernelBoundary, self.params) else: - local_matrix_zeroExterior = self.getLocalMatrixBoundaryZeroExterior(kernel.getModifiedKernel(horizon=constant(np.inf)).getBoundaryKernel(), self.params) + local_matrix_zeroExterior = self.getLocalMatrixBoundaryZeroExterior(kernelInfHorizonBoundary, self.params) if type(local_matrix_zeroExterior) != type(self.local_matrix_zeroExterior): self.local_matrix_zeroExterior = local_matrix_zeroExterior - self.local_matrix_surface = self.getLocalMatrixBoundaryZeroExterior(kernel.getBoundaryKernel(), self.params) + self.local_matrix_surface = self.getLocalMatrixBoundaryZeroExterior(kernelBoundary, self.params) else: - self.local_matrix_zeroExterior.setKernel(kernel.getModifiedKernel(horizon=constant(np.inf)).getBoundaryKernel(), target_order=self.params.get('target_order', None)) - self.local_matrix_surface.setKernel(kernel.getBoundaryKernel(), target_order=self.params.get('target_order', None)) + self.local_matrix_zeroExterior.setKernel(kernelInfHorizonBoundary, target_order=self.params.get('target_order', None)) + self.local_matrix_surface.setKernel(kernelBoundary, target_order=self.params.get('target_order', None)) if self.local_matrix_zeroExterior is not None: self.local_matrix_zeroExterior.setMesh1(self.dm.mesh) @@ -1025,6 +1028,7 @@ cdef class {SCALAR_label}nonlocalBuilder: cdef inline {SCALAR_label}double_local_matrix_t getLocalMatrixBoundaryZeroExterior(self, {SCALAR_label}Kernel kernelBoundary, dict params): cdef: fractionalOrderBase s + REAL_t max_singularity opType = params.get('opType', 'Laplacian') target_order = params.get('target_order', None) if 'quadTypeBoundary' in params: @@ -1837,7 +1841,7 @@ cdef class {SCALAR_label}nonlocalBuilder: self.PLogger.addValue('specialQuadRules', len(self.local_matrix.specialQuadRules)) self.PLogger.addValue('distantQuadRules', len(self.local_matrix.distantQuadRules)) - if not self.kernel.variable: + if not self.kernel.variableSingularity: if not self.kernel.complement: with self.PLogger.Timer(prefix+'cluster zeroExterior'): # This corresponds to @@ -1926,7 +1930,12 @@ cdef class {SCALAR_label}nonlocalBuilder: x = np.zeros((self.mesh.dim), dtype=REAL) y = np.zeros((self.mesh.dim), dtype=REAL) y[0] = self.kernel.horizonValue - coeff = constant(-vol*self.local_matrix_zeroExterior.kernel(x, y)) + assert self.local_matrix_zeroExterior.kernel.valueSize == self.mesh.dim + n = np.zeros((self.mesh.dim), dtype=REAL) + n[0] = -1. + self.local_matrix_zeroExterior.kernel.setNormal(n) + val = self.local_matrix_zeroExterior.kernel(x, y) + coeff = constant(vol*val) qr = simplexXiaoGimbutas(2, self.mesh.dim) if self.mesh.dim == 1: mass = mass_1d_sym_scalar_anisotropic(coeff, self.dm, qr) @@ -2620,7 +2629,7 @@ cdef class {SCALAR_label}nonlocalBuilder: else: rank_root = global_root - if self.kernel.variable and not (self.kernel.variableOrder and isinstance(self.kernel.s, singleVariableUnsymmetricFractionalOrder)): + if self.kernel.variableSingularity and not isinstance(self.kernel.s, singleVariableUnsymmetricFractionalOrder): blocks, jumps = self.getKernelBlocksAndJumps() if len(jumps) > 0: my_id = global_root.get_max_id()+1 @@ -2771,8 +2780,6 @@ cdef class {SCALAR_label}nonlocalBuilder: # get admissible cluster pairs for n in global_root.children: - if ignoreDiagonalBlocks and (n.id == rank_root.id): - continue getAdmissibleClusters(self.kernel, rank_root, n, Pfar=Pfar, Pnear=Pnear) @@ -3000,7 +3007,7 @@ cdef class {SCALAR_label}nonlocalBuilder: refParams.interpolation_order = iO mL = self.params.get('maxLevels', None) if mL is None: - # maxLevels = max(int(np.around(np.log2(DoFMap.num_dofs)/mesh.dim-np.log2(refParams.interpolation_order))), 0) + # maxLevels = max(int(np.around(np.log2(DoFMap.num_dofs)/mesh.manifold_dim-np.log2(refParams.interpolation_order))), 0) refParams.maxLevels = 200 else: refParams.maxLevels = mL @@ -3010,7 +3017,7 @@ cdef class {SCALAR_label}nonlocalBuilder: # For this value, size(kernelInterpolant) == size(dense block) # If we choose a smaller value for minFarFieldBlockSize, then we use more memory, # but we might save time, since the assembly of a far field block is cheaper than a near field block. - # refParams.farFieldInteractionSize = refParams.interpolation_order**(2*mesh.dim) + # refParams.farFieldInteractionSize = refParams.interpolation_order**(2*mesh.manifold_dim) refParams.farFieldInteractionSize = -1 else: refParams.farFieldInteractionSize = mFFBS @@ -3020,9 +3027,9 @@ cdef class {SCALAR_label}nonlocalBuilder: if refParams.farFieldInteractionSize < 0: loggamma = abs(np.log(0.25)) interpolation_order = max(np.ceil((2*target_order+max(-singularity, 2))*abs(np.log(mesh.h/mesh.diam))/loggamma/3.), 2) - refParams.minSize = interpolation_order**mesh.dim//2 + refParams.minSize = interpolation_order**mesh.manifold_dim//2 else: - refParams.minSize = refParams.interpolation_order**mesh.dim//2 + refParams.minSize = refParams.interpolation_order**mesh.manifold_dim//2 else: refParams.minSize = mCS if self.kernel.finiteHorizon: diff --git a/nl/PyNucleus_nl/nonlocalOperator.pxd b/nl/PyNucleus_nl/nonlocalOperator.pxd index 469d0e41..342b50fc 100644 --- a/nl/PyNucleus_nl/nonlocalOperator.pxd +++ b/nl/PyNucleus_nl/nonlocalOperator.pxd @@ -20,22 +20,26 @@ from PyNucleus_fem.meshCy cimport (volume0Dsimplex, volume2Dsimplex, volume1Din2Dsimplex, volume3Dsimplex, - volume2Din3Dsimplex) + volume2Din3Dsimplex, + volume1Din3Dsimplex) from . twoPointFunctions cimport (twoPointFunction, constantTwoPoint) from . interactionDomains cimport REMOTE from . fractionalOrders cimport (fractionalOrderBase, constFractionalOrder, variableFractionalOrder) -from . kernelsCy cimport (Kernel, - ComplexKernel, - FractionalKernel) -from . clusterMethodCy cimport tree_node +from . kernels cimport (Kernel, + ComplexKernel, + FractionalKernel) +from . clusterMethod cimport tree_node ctypedef INDEX_t panelType from . bitset cimport MASK_t +cdef INDEX_t my_binom(INDEX_t a, INDEX_t b) + + cdef class PermutationIndexer: cdef: INDEX_t N @@ -59,6 +63,12 @@ cdef class specialQuadRule: public transformQuadratureRule qrTransformed1 +cdef class singularityCancelationQuadRule(quadratureRule): + cdef: + public REAL_t[::1] singularPart + cdef void scaleWeights(self, scaling) + + cdef class nonlocalLaplacian1D(nonlocalOperator): cdef: public REAL_t target_order, quad_order_diagonal @@ -70,5 +80,3 @@ cdef class nonlocalLaplacian2D(nonlocalOperator): cdef: public REAL_t target_order, quad_order_diagonal, quad_order_diagonalV INDEX_t[::1] idx1, idx2, idx3, idx4 - - diff --git a/nl/PyNucleus_nl/nonlocalOperator.pyx b/nl/PyNucleus_nl/nonlocalOperator.pyx index 5e8b70d5..f453debe 100644 --- a/nl/PyNucleus_nl/nonlocalOperator.pyx +++ b/nl/PyNucleus_nl/nonlocalOperator.pyx @@ -7,13 +7,14 @@ import numpy as np cimport numpy as np -from libc.math cimport sqrt, ceil, fabs as abs +from libc.math cimport sqrt, ceil, fabs as abs, log from scipy.special import factorial from PyNucleus_base.myTypes import INDEX, REAL, COMPLEX from PyNucleus_base import uninitialized from PyNucleus_base.blas cimport mydot from cpython.mem cimport PyMem_Malloc, PyMem_Free from . interactionDomains cimport CUT +from . kernelNormalization cimport constantFractionalLaplacianScaling, variableFractionalLaplacianScaling cdef: MASK_t ALL @@ -25,6 +26,18 @@ cdef INDEX_t MAX_INT = np.iinfo(INDEX).max cdef REAL_t inf = np.inf +cdef INDEX_t my_binom(INDEX_t a, INDEX_t b): + if (a == 0) or (a == 1): + return 1 + elif a == 2: + if b == 1: + return 2 + else: + return 1 + else: + return -1 + + cdef inline void getSimplexAndCenter(const INDEX_t[:, ::1] cells, const REAL_t[:, ::1] vertices, const INDEX_t cellIdx, @@ -104,6 +117,14 @@ cdef class specialQuadRule: self.PHI3 = PHI3 +cdef class singularityCancelationQuadRule(quadratureRule): + cdef void scaleWeights(self, scaling): + cdef: + INDEX_t i + for i in range(self.num_nodes): + self.weights[i] *= scaling + + cdef panelType MAX_PANEL = 120 diff --git a/nl/PyNucleus_nl/nonlocalOperator_decl_{SCALAR}.pxi b/nl/PyNucleus_nl/nonlocalOperator_decl_{SCALAR}.pxi index 965c56a4..10cff32e 100644 --- a/nl/PyNucleus_nl/nonlocalOperator_decl_{SCALAR}.pxi +++ b/nl/PyNucleus_nl/nonlocalOperator_decl_{SCALAR}.pxi @@ -79,6 +79,13 @@ cdef class {SCALAR_label}nonlocalOperator({SCALAR_label}double_local_matrix_t): {SCALAR}_t[:, ::1] temp {SCALAR}_t[:, ::1] temp2 public REAL_t[::1] n, w + public quadratureRule qrVolume, qrFace, qrEdge, qrVertex, qr + public REAL_t[:, ::1] PSI_volume, PSI_face, PSI_edge, PSI_vertex + public REAL_t[:, :, ::1] PHI_volume, PHI_face, PHI_edge, PHI_vertex + public REAL_t[:, ::1] PHI_face2, PHI_edge2, PHI_vertex2 + REAL_t[::1] scaling_values + BOOL_t multiLogKernel + INDEX_t termNo cpdef void setKernel(self, {SCALAR_label}Kernel kernel, quad_order_diagonal=*, target_order=*) cdef void getNearQuadRule(self, panelType panel) cdef inline shapeFunction getLocalShapeFunction(self, INDEX_t local_dof) @@ -86,6 +93,11 @@ cdef class {SCALAR_label}nonlocalOperator({SCALAR_label}double_local_matrix_t): cdef void addQuadRule_nonSym(self, panelType panel) cdef void addQuadRule_boundary(self, panelType panel) cdef void getNonSingularNearQuadRule(self, panelType panel) - cdef void eval_distant(self, {SCALAR}_t[:, ::1] contrib, panelType panel, MASK_t mask=*) + cdef void eval_distant_sym(self, {SCALAR}_t[:, ::1] contrib, panelType panel, MASK_t mask=*) + cdef void eval_near_sym(self, {SCALAR}_t[:, ::1] contrib, panelType panel, MASK_t mask=*) cdef void eval_distant_nonsym(self, {SCALAR}_t[:, ::1] contrib, panelType panel, MASK_t mask=*) + cdef void eval_near_nonsym(self, {SCALAR}_t[:, ::1] contrib, panelType panel, MASK_t mask=*) + cdef void eval_distant_nonsym2(self, {SCALAR}_t[:, ::1] contrib, panelType panel, MASK_t mask=*) + cdef void eval_near_nonsym2(self, {SCALAR}_t[:, ::1] contrib, panelType panel, MASK_t mask=*) cdef void eval_distant_boundary(self, {SCALAR}_t[:, ::1] contrib, panelType panel, MASK_t mask=*) + cdef void eval_near_boundary(self, {SCALAR}_t[:, ::1] contrib, panelType panel, MASK_t mask=*) diff --git a/nl/PyNucleus_nl/nonlocalOperator_{SCALAR}.pxi b/nl/PyNucleus_nl/nonlocalOperator_{SCALAR}.pxi index c8a3ba54..b31be397 100644 --- a/nl/PyNucleus_nl/nonlocalOperator_{SCALAR}.pxi +++ b/nl/PyNucleus_nl/nonlocalOperator_{SCALAR}.pxi @@ -34,7 +34,7 @@ cdef class {SCALAR_label}double_local_matrix_t: elif dim == 3 and manifold_dim1 == 2: self.volume1 = volume2Din3Dsimplex else: - raise NotImplementedError() + raise NotImplementedError((dim, manifold_dim1)) if dim == 1 and manifold_dim2 == 1: self.volume2 = volume1Dsimplex @@ -44,12 +44,16 @@ cdef class {SCALAR_label}double_local_matrix_t: self.volume2 = volume2Dsimplex elif dim == 2 and manifold_dim2 == 1: self.volume2 = volume1Din2Dsimplex + elif dim == 2 and manifold_dim2 == 0: + self.volume2 = volume0Dsimplex elif dim == 3 and manifold_dim2 == 3: self.volume2 = volume3Dsimplex elif dim == 3 and manifold_dim2 == 2: self.volume2 = volume2Din3Dsimplex + elif dim == 3 and manifold_dim2 == 1: + self.volume2 = volume1Din3Dsimplex else: - raise NotImplementedError() + raise NotImplementedError((dim, manifold_dim2)) if manifold_dim1 == 1: self.IDENTICAL = COMMON_EDGE @@ -58,7 +62,7 @@ cdef class {SCALAR_label}double_local_matrix_t: elif manifold_dim1 == 3: self.IDENTICAL = COMMON_VOLUME else: - raise NotImplementedError() + raise NotImplementedError(manifold_dim1) self.center1 = uninitialized((self.dim), dtype=REAL) self.center2 = uninitialized((self.dim), dtype=REAL) @@ -163,11 +167,12 @@ cdef class {SCALAR_label}double_local_matrix_t: cdef void setMesh2(self, meshBase mesh2): self.setVerticesCells2(mesh2.vertices, mesh2.cells) - if mesh2.manifold_dim > 0: - h2 = 2.*mesh2.h - self.h2MaxInv = 1./h2 - else: - self.h2MaxInv = 1. + if mesh2.num_cells > 0: + if mesh2.manifold_dim > 0: + h2 = 2.*mesh2.h + self.h2MaxInv = 1./h2 + else: + self.h2MaxInv = 1. cdef void setVerticesCells2(self, REAL_t[:, ::1] vertices2, INDEX_t[:, ::1] cells2): self.vertices2 = vertices2 @@ -464,6 +469,22 @@ cdef class {SCALAR_label}nonlocalOperator({SCALAR_label}double_local_matrix_t): if self.kernel.variableHorizon: self.symmetricCells = False + self.multiLogKernel = False + self.scaling_values = None + self.termNo = -1 + if isinstance(self.kernel.scalingPrePhi, constantFractionalLaplacianScaling): + scal = self.kernel.scalingPrePhi + if scal.values.shape[0] > 1: + self.multiLogKernel = True + self.scaling_values = scal.values + self.termNo = scal.termNo + elif isinstance(self.kernel.scalingPrePhi, variableFractionalLaplacianScaling): + scalVar = self.kernel.scalingPrePhi + if scalVar.values.shape[0] > 1: + self.multiLogKernel = True + self.scaling_values = scalVar.values + self.termNo = scalVar.termNo + cpdef void setKernel(self, {SCALAR_label}Kernel kernel, quad_order_diagonal=None, target_order=None): raise NotImplementedError() @@ -508,9 +529,9 @@ cdef class {SCALAR_label}nonlocalOperator({SCALAR_label}double_local_matrix_t): if self.kernel.variable: if self.kernel.piecewise: - self.kernel.evalParams(self.center1, self.center2) + self.kernel.evalParamsPtr(self.dim, &self.center1[0], &self.center2[0]) else: - self.kernel.evalParamsOnSimplices(self.center1, self.center2, self.simplex1, self.simplex2) + self.kernel.evalParamsOnSimplicesPtr(self.dim, &self.center1[0], &self.center2[0], &self.simplex1[0, 0], &self.simplex2[0, 0]) if panel == DISTANT: if self.kernel.interaction.getRelativePosition(self.simplex1, self.simplex2) == REMOTE: @@ -719,10 +740,12 @@ cdef class {SCALAR_label}nonlocalOperator({SCALAR_label}double_local_matrix_t): if qr2.num_nodes > self.temp.shape[0]: self.temp = uninitialized((qr2.num_nodes, self.kernel.valueSize), dtype={SCALAR}) - cdef void eval_distant(self, - {SCALAR}_t[:, ::1] contrib, - panelType panel, - MASK_t mask=ALL): + cdef void eval_distant_sym(self, + {SCALAR}_t[:, ::1] contrib, + panelType panel, + MASK_t mask=ALL): + # Evaluates 0.5 \\int_{K_1} \\int_{K_1} [ u(x) - u(y) ] [ v(x) - v(y) ] \\gamma(x, y) dy dx + # for symmetric kernel \\gamma. cdef: INDEX_t k, i, j, I, J, l REAL_t vol, vol1 = self.vol1, vol2 = self.vol2 @@ -742,6 +765,7 @@ cdef class {SCALAR_label}nonlocalOperator({SCALAR_label}double_local_matrix_t): REAL_t[:, ::1] A1, A2 BOOL_t cutElements = False INDEX_t valueSize = self.kernel.valueSize + {SCALAR}_t fac if self.kernel.finiteHorizon: # check if the horizon might cut the elements @@ -757,6 +781,7 @@ cdef class {SCALAR_label}nonlocalOperator({SCALAR_label}double_local_matrix_t): if not cutElements: vol = vol1*vol2 + vol *= 0.5 if panel < 0: sQR = (self.distantQuadRulesPtr[MAX_PANEL+panel]) else: @@ -773,6 +798,10 @@ cdef class {SCALAR_label}nonlocalOperator({SCALAR_label}double_local_matrix_t): &self.x[i, 0], &self.y[j, 0], &self.vec[0]) + if self.multiLogKernel: + fac = self.scaling_values[self.termNo] + for l in range(valueSize): + self.vec[l] *= fac for l in range(valueSize): self.temp[k, l] = qr2.weights[k]*self.vec[l] k += 1 @@ -810,6 +839,7 @@ cdef class {SCALAR_label}nonlocalOperator({SCALAR_label}double_local_matrix_t): numQuadNodes1 = qr1trans.num_nodes vol = vol1*vol2 + vol *= 0.5 dofs_per_element = self.DoFMap.dofs_per_element A1 = a_A1 @@ -846,10 +876,104 @@ cdef class {SCALAR_label}nonlocalOperator({SCALAR_label}double_local_matrix_t): contrib[k, 0] += val * PSI_I*PSI_J k += 1 + cdef void eval_near_sym(self, + {SCALAR}_t[:, ::1] contrib, + panelType panel, + MASK_t mask=ALL): + # Evaluates 0.5 \\int_{K_1} \\int_{K_1} [ u(x) - u(y) ] [ v(x) - v(y) ] \\gamma(x, y) dy dx + # for symmetric kernel \\gamma. + cdef: + INDEX_t k, m, i, j, I, J, dofs_per_element = self.DoFMap.dofs_per_element, dim = self.DoFMap.mesh.dim, l + INDEX_t valueSize = self.kernel.valueSize + REAL_t[:, ::1] simplex1 = self.simplex1 + REAL_t[:, ::1] simplex2 = self.simplex2 + REAL_t vol + {SCALAR}_t val + singularityCancelationQuadRule qr + REAL_t[:, ::1] PSI + REAL_t x[3] + REAL_t y[3] + REAL_t z, neg_log_z, neg_log_N, fac + INDEX_t beta + + if panel == COMMON_VOLUME: + qr = self.qrVolume + PSI = self.PSI_volume + elif panel == COMMON_FACE: + qr = self.qrFace + PSI = self.PSI_face + elif panel == COMMON_EDGE: + qr = self.qrEdge + PSI = self.PSI_edge + elif panel == COMMON_VERTEX: + qr = self.qrVertex + PSI = self.PSI_vertex + else: + raise NotImplementedError('Unknown panel type: {}'.format(panel)) + + vol = 0.5*self.vol1*self.vol2 + + # Evaluate the kernel on the quadrature nodes + for m in range(qr.num_nodes): + for j in range(dim): + x[j] = 0. + for k in range(self.manifold_dim1+1): + x[j] += simplex1[self.perm1[k], j]*qr.nodes[k, m] + y[j] = 0. + for k in range(self.manifold_dim2+1): + y[j] += simplex2[self.perm2[k], j]*qr.nodes[self.manifold_dim1+1+k, m] + + self.kernel.evalPtr(dim, + &x[0], + &y[0], + &self.vec[0]) + + if self.multiLogKernel: + # get non-vanishing part N of |x-y| + z = 0. + for j in range(dim): + z += (x[j]-y[j])**2 + neg_log_z = -0.5*log(z) + neg_log_N = neg_log_z+log(qr.singularPart[m]) + + fac = 0. + for beta in range(self.termNo, self.scaling_values.shape[0]): + fac += my_binom(beta, self.termNo) * self.scaling_values[beta] * neg_log_N**(beta-self.termNo) + fac *= neg_log_z**(-self.termNo) + for l in range(valueSize): + self.vec[l] *= fac + + for l in range(valueSize): + self.temp[m, l] = qr.weights[m] * self.vec[l] + + # "perm" maps from dofs on the reordered simplices (matching + # vertices first) to the dofs in the usual ordering. + contrib[:] = 0. + for I in range(PSI.shape[0]): + i = self.perm[I] + for J in range(I, PSI.shape[0]): + j = self.perm[J] + # We are assembling the upper trinagular part of the + # symmetric (2*dofs_per_element)**2 local stiffness + # matrix. This computes the flattened index. + if j < i: + k = 2*dofs_per_element*j-(j*(j+1) >> 1) + i + else: + k = 2*dofs_per_element*i-(i*(i+1) >> 1) + j + # Check if that entry has been requested. + if mask[k]: + for l in range(valueSize): + val = 0. + for m in range(qr.num_nodes): + val += self.temp[m, l] * PSI[I, m] * PSI[J, m] + contrib[k, l] = val*vol + cdef void eval_distant_nonsym(self, {SCALAR}_t[:, ::1] contrib, panelType panel, MASK_t mask=ALL): + # Evaluates 0.5 \\int_{K_1} \\int_{K_1} [ u(x) \\gamma(x, y) - u(y) \\gamma(y, x) ] [ v(x) - v(y) ] dy dx + # for non-symmetric kernel \\gamma. cdef: INDEX_t k, i, j, I, J, l REAL_t vol, vol1 = self.vol1, vol2 = self.vol2 @@ -871,6 +995,7 @@ cdef class {SCALAR_label}nonlocalOperator({SCALAR_label}double_local_matrix_t): REAL_t[::1] b1 REAL_t[:, ::1] A1, A2 INDEX_t valueSize = self.kernel.valueSize + {SCALAR}_t fac if self.kernel.finiteHorizon: # check if the horizon might cut the elements @@ -886,6 +1011,7 @@ cdef class {SCALAR_label}nonlocalOperator({SCALAR_label}double_local_matrix_t): if not cutElements: vol = vol1*vol2 + vol *= 0.5 if panel < 0: sQR = (self.distantQuadRulesPtr[MAX_PANEL+panel]) else: @@ -904,10 +1030,18 @@ cdef class {SCALAR_label}nonlocalOperator({SCALAR_label}double_local_matrix_t): &self.x[i, 0], &self.y[j, 0], &self.vec[0]) + if self.multiLogKernel: + fac = self.scaling_values[self.termNo] + for l in range(valueSize): + self.vec[l] *= fac self.kernel.evalPtr(dim, &self.y[j, 0], &self.x[i, 0], &self.vec2[0]) + if self.multiLogKernel: + fac = self.scaling_values[self.termNo] + for l in range(valueSize): + self.vec2[l] *= fac for l in range(valueSize): self.temp[k, l] = w * self.vec[l] self.temp2[k, l] = w * self.vec2[l] @@ -946,6 +1080,7 @@ cdef class {SCALAR_label}nonlocalOperator({SCALAR_label}double_local_matrix_t): numQuadNodes1 = qr1trans.num_nodes vol = vol1*vol2 + vol *= 0.5 dofs_per_element = self.DoFMap.dofs_per_element A1 = a_A1 @@ -985,6 +1120,304 @@ cdef class {SCALAR_label}nonlocalOperator({SCALAR_label}double_local_matrix_t): contrib[k, 0] += (val * PHI_I_0 - val2 * PHI_I_1) * PSI_J k += 1 + cdef void eval_near_nonsym(self, + {SCALAR}_t[:, ::1] contrib, + panelType panel, + MASK_t mask=ALL): + # Evaluates 0.5 \\int_{K_1} \\int_{K_1} [ u(x) \\gamma(x, y) - u(y) \\gamma(y, x) ] [ v(x) - v(y) ] dy dx + # for non-symmetric kernel \\gamma. + cdef: + INDEX_t k, m, i, j, I, J, dofs_per_element, dim = self.DoFMap.mesh.dim, l + REAL_t[:, ::1] simplex1 = self.simplex1 + REAL_t[:, ::1] simplex2 = self.simplex2 + REAL_t vol + {SCALAR}_t val + singularityCancelationQuadRule qr + REAL_t[:, :, ::1] PHI + REAL_t x[3] + REAL_t y[3] + INDEX_t valueSize = self.kernel.valueSize + REAL_t z, neg_log_z, neg_log_N, fac + INDEX_t beta + + if panel == COMMON_VOLUME: + qr = self.qrVolume + PHI = self.PHI_volume + elif panel == COMMON_FACE: + qr = self.qrFace + PHI = self.PHI_face + elif panel == COMMON_EDGE: + qr = self.qrEdge + PHI = self.PHI_edge + elif panel == COMMON_VERTEX: + qr = self.qrVertex + PHI = self.PHI_vertex + else: + raise NotImplementedError('Unknown panel type: {}'.format(panel)) + + vol = 0.5*self.vol1*self.vol2 + + dofs_per_element = self.DoFMap.dofs_per_element + + # Evaluate the kernel on the quadrature nodes + for m in range(qr.num_nodes): + for j in range(dim): + x[j] = 0. + for k in range(self.manifold_dim1+1): + x[j] += simplex1[self.perm1[k], j]*qr.nodes[k, m] + y[j] = 0. + for k in range(self.manifold_dim2+1): + y[j] += simplex2[self.perm2[k], j]*qr.nodes[self.manifold_dim1+1+k, m] + + self.kernel.evalPtr(dim, &x[0], &y[0], &self.vec[0]) + if self.multiLogKernel: + # get non-vanishing part N of |x-y| + z = 0. + for j in range(dim): + z += (x[j]-y[j])**2 + neg_log_z = -0.5*log(z) + neg_log_N = neg_log_z+log(qr.singularPart[m]) + + fac = 0. + for beta in range(self.termNo, self.scaling_values.shape[0]): + fac += my_binom(beta, self.termNo) * self.scaling_values[beta] * neg_log_N**(beta-self.termNo) + fac *= neg_log_z**(-self.termNo) + for l in range(valueSize): + self.vec[l] *= fac + + self.kernel.evalPtr(dim, &y[0], &x[0], &self.vec2[0]) + if self.multiLogKernel: + fac = 0. + for beta in range(self.termNo, self.scaling_values.shape[0]): + fac += my_binom(beta, self.termNo) * self.scaling_values[beta] * neg_log_N**(beta-self.termNo) + fac *= neg_log_z**(-self.termNo) + for l in range(valueSize): + self.vec2[l] *= fac + + for l in range(valueSize): + self.temp[m, l] = qr.weights[m] * self.vec[l] + self.temp2[m, l] = qr.weights[m] * self.vec2[l] + + # "perm" maps from dofs on the reordered simplices (matching + # vertices first) to the dofs in the usual ordering. + contrib[:] = 0. + for I in range(PHI.shape[0]): + i = self.perm[I] + for J in range(PHI.shape[0]): + j = self.perm[J] + k = i*(2*dofs_per_element)+j + # Check if that entry has been requested. + if mask[k]: + for l in range(valueSize): + val = 0. + for m in range(qr.num_nodes): + val += (self.temp[m, l] * PHI[I, m, 0] - self.temp2[m, l] * PHI[I, m, 1]) * (PHI[J, m, 0] - PHI[J, m, 1]) + contrib[k, l] = val*vol + + cdef void eval_distant_nonsym2(self, + {SCALAR}_t[:, ::1] contrib, + panelType panel, + MASK_t mask=ALL): + # Evaluates 0.5 \\int_{K_1} \\int_{K_1} [ u(x) - u(y) ] [ v(x) - v(y) ] \\gamma(x, y) dy dx + # for non-symmetric kernel \\gamma. + cdef: + INDEX_t k, i, j, I, J, l + REAL_t vol, vol1 = self.vol1, vol2 = self.vol2 + {SCALAR}_t val + doubleSimplexQuadratureRule qr2 + REAL_t[:, ::1] PSI + REAL_t[:, :, ::1] PHI + REAL_t[:, ::1] simplex1 = self.simplex1 + REAL_t[:, ::1] simplex2 = self.simplex2 + INDEX_t dim = simplex1.shape[1] + BOOL_t cutElements = False + REAL_t w + REAL_t c1, c2, PHI_I_0, PHI_I_1, PSI_J + transformQuadratureRule qr0trans, qr1trans + INDEX_t dofs_per_element, numQuadNodes0, numQuadNodes1 + REAL_t a_b1[3] + REAL_t a_A1[3][3] + REAL_t a_A2[3][3] + REAL_t[::1] b1 + REAL_t[:, ::1] A1, A2 + INDEX_t valueSize = self.kernel.valueSize + + if self.kernel.finiteHorizon: + # check if the horizon might cut the elements + if self.kernel.interaction.relPos == CUT: + cutElements = True + if self.kernel.complement: + cutElements = False + # TODO: cutElements should be set to True, but + # need to figure out the element + # transformation. + + contrib[:, :] = 0. + + if not cutElements: + vol = vol1*vol2 + vol *= 0.5 + if panel < 0: + sQR = (self.distantQuadRulesPtr[MAX_PANEL+panel]) + else: + sQR = (self.distantQuadRulesPtr[panel]) + qr2 = (sQR.qr) + PSI = sQR.PSI + PHI = sQR.PHI3 + qr2.rule1.nodesInGlobalCoords(simplex1, self.x) + qr2.rule2.nodesInGlobalCoords(simplex2, self.y) + + k = 0 + for i in range(qr2.rule1.num_nodes): + for j in range(qr2.rule2.num_nodes): + w = qr2.weights[k] + self.kernel.evalPtr(dim, + &self.x[i, 0], + &self.y[j, 0], + &self.vec[0]) + self.kernel.evalPtr(dim, + &self.y[j, 0], + &self.x[i, 0], + &self.vec2[0]) + for l in range(valueSize): + self.temp[k, l] = w * self.vec[l] + k += 1 + + k = 0 + for I in range(2*self.DoFMap.dofs_per_element): + for J in range(2*self.DoFMap.dofs_per_element): + if mask[k]: + for l in range(valueSize): + val = 0. + for i in range(qr2.num_nodes): + val += self.temp[i, l] * (PHI[0, I, i] - PHI[1, I, i]) * PSI[J, i] + contrib[k, l] = val*vol + k += 1 + else: + if panel < 0: + sQR = (self.distantQuadRulesPtr[MAX_PANEL+panel]) + else: + sQR = (self.distantQuadRulesPtr[panel]) + qr2 = (sQR.qr) + if sQR.qrTransformed0 is not None: + qr0trans = sQR.qrTransformed0 + else: + qr0 = qr2.rule1 + qr0trans = transformQuadratureRule(qr0) + sQR.qrTransformed0 = qr0trans + if sQR.qrTransformed1 is not None: + qr1trans = sQR.qrTransformed1 + else: + qr1 = qr2.rule2 + qr1trans = transformQuadratureRule(qr1) + sQR.qrTransformed1 = qr1trans + + numQuadNodes0 = qr0trans.num_nodes + numQuadNodes1 = qr1trans.num_nodes + + vol = vol1*vol2 + vol *= 0.5 + dofs_per_element = self.DoFMap.dofs_per_element + + A1 = a_A1 + b1 = a_b1 + A2 = a_A2 + + self.kernel.interaction.startLoopSubSimplices_Simplex(simplex1, simplex2) + while self.kernel.interaction.nextSubSimplex_Simplex(A1, b1, &c1): + qr0trans.setAffineBaryTransform(A1, b1) + qr0trans.nodesInGlobalCoords(simplex1, self.x) + for i in range(qr0trans.num_nodes): + self.kernel.interaction.startLoopSubSimplices_Node(self.x[i, :], simplex2) + while self.kernel.interaction.nextSubSimplex_Node(A2, &c2): + qr1trans.setLinearBaryTransform(A2) + qr1trans.nodesInGlobalCoords(simplex2, self.y) + for j in range(qr1trans.num_nodes): + w = qr0trans.weights[i]*qr1trans.weights[j]*c1 * c2 * vol + self.kernel.evalPtr(dim, &self.x[i, 0], &self.y[j, 0], &self.vec[0]) + val = w*self.vec[0] + k = 0 + for I in range(2*dofs_per_element): + if I < dofs_per_element: + self.getLocalShapeFunction(I).evalStrided(&qr0trans.nodes[0, i], NULL, numQuadNodes0, &PHI_I_0) + PHI_I_1 = 0. + else: + PHI_I_0 = 0. + self.getLocalShapeFunction(I-dofs_per_element).evalStrided(&qr1trans.nodes[0, j], NULL, numQuadNodes1, &PHI_I_1) + for J in range(2*dofs_per_element): + if mask[k]: + if J < dofs_per_element: + self.getLocalShapeFunction(J).evalStrided(&qr0trans.nodes[0, i], NULL, numQuadNodes0, &PSI_J) + else: + self.getLocalShapeFunction(J-dofs_per_element).evalStrided(&qr1trans.nodes[0, j], NULL, numQuadNodes1, &PSI_J) + PSI_J *= -1 + contrib[k, 0] += val * (PHI_I_0 - PHI_I_1) * PSI_J + k += 1 + + cdef void eval_near_nonsym2(self, + {SCALAR}_t[:, ::1] contrib, + panelType panel, + MASK_t mask=ALL): + # Evaluates 0.5 \\int_{K_1} \\int_{K_1} [ u(x) - u(y) ] [ v(x) - v(y) ] \\gamma(x, y) dy dx + # for non-symmetric kernel \\gamma. + cdef: + INDEX_t k, m, i, j, I, J, dofs_per_element = self.DoFMap.dofs_per_element, dim = self.DoFMap.mesh.dim, l + REAL_t[:, ::1] simplex1 = self.simplex1 + REAL_t[:, ::1] simplex2 = self.simplex2 + REAL_t vol + {SCALAR}_t val + quadratureRule qr + REAL_t[:, :, ::1] PHI + REAL_t x[3] + REAL_t y[3] + INDEX_t valueSize = self.kernel.valueSize + + if panel == COMMON_VOLUME: + qr = self.qrVolume + PHI = self.PHI_volume + elif panel == COMMON_FACE: + qr = self.qrFace + PHI = self.PHI_face + elif panel == COMMON_EDGE: + qr = self.qrEdge + PHI = self.PHI_edge + elif panel == COMMON_VERTEX: + qr = self.qrVertex + PHI = self.PHI_vertex + else: + raise NotImplementedError('Unknown panel type: {}'.format(panel)) + + vol = 0.5*self.vol1*self.vol2 + + # Evaluate the kernel on the quadrature nodes + for m in range(qr.num_nodes): + for j in range(dim): + x[j] = 0. + for k in range(self.manifold_dim1+1): + x[j] += simplex1[self.perm1[k], j]*qr.nodes[k, m] + y[j] = 0. + for k in range(self.manifold_dim2+1): + y[j] += simplex2[self.perm2[k], j]*qr.nodes[self.manifold_dim1+1+k, m] + self.kernel.evalPtr(dim, &x[0], &y[0], &self.vec[0]) + for l in range(valueSize): + self.temp[m, l] = qr.weights[m] * self.vec[l] + + # "perm" maps from dofs on the reordered simplices (matching + # vertices first) to the dofs in the usual ordering. + contrib[:] = 0. + for I in range(PHI.shape[0]): + i = self.perm[I] + for J in range(PHI.shape[0]): + j = self.perm[J] + k = i*(2*dofs_per_element)+j + # Check if that entry has been requested. + if mask[k]: + for l in range(valueSize): + val = 0. + for m in range(qr.num_nodes): + val += self.temp[m, l] * (PHI[I, m, 0] - PHI[I, m, 1]) * (PHI[J, m, 0] - PHI[J, m, 1]) + contrib[k, l] = val*vol + cdef void addQuadRule_boundary(self, panelType panel): cdef: simplexQuadratureRule qr0, qr1 @@ -995,8 +1428,8 @@ cdef class {SCALAR_label}nonlocalOperator({SCALAR_label}double_local_matrix_t): REAL_t lcl_bary_x[4] shapeFunction sf REAL_t phi_x - qr0 = simplexXiaoGimbutas(panel, self.dim) - qr1 = simplexDuffyTransformation(panel, self.dim, self.dim-1) + qr0 = simplexXiaoGimbutas(panel, self.dim, self.manifold_dim1) + qr1 = simplexDuffyTransformation(panel, self.dim, self.manifold_dim2) qr2 = doubleSimplexQuadratureRule(qr0, qr1) PHI = uninitialized((self.DoFMap.dofs_per_element, qr2.num_nodes), dtype=REAL) for i in range(self.DoFMap.dofs_per_element): @@ -1023,6 +1456,7 @@ cdef class {SCALAR_label}nonlocalOperator({SCALAR_label}double_local_matrix_t): {SCALAR}_t[:, ::1] contrib, panelType panel, MASK_t mask=ALL): + # Evaluates \\int_{K} u(x) v(x) \\int_{e} \\gamma(x, y) dy dx cdef: INDEX_t k, m, i, j, I, J, l REAL_t vol, valReal, vol1 = self.vol1, vol2 = self.vol2 @@ -1032,39 +1466,10 @@ cdef class {SCALAR_label}nonlocalOperator({SCALAR_label}double_local_matrix_t): REAL_t[:, ::1] simplex1 = self.simplex1 REAL_t[:, ::1] simplex2 = self.simplex2 INDEX_t dim = simplex1.shape[1] - REAL_t normW, nw INDEX_t valueSize = self.kernel.valueSize + {SCALAR}_t fac - # Kernel: - # \Gamma(x,y) = n \dot (x-y) * C(d,s) / (2s) / |x-y|^{d+2s} - # with inward normal n. - # - # Rewrite as - # \Gamma(x,y) = [ n \dot (x-y)/|x-y| ] * [ C(d,s) / (2s) / |x-y|^{d-1+2s} ] - # \--------------------------------/ - # | - # boundaryKernel - # - # n is independent of x and y - if dim == 2: - self.n[0] = simplex2[1, 1] - simplex2[0, 1] - self.n[1] = simplex2[0, 0] - simplex2[1, 0] - # F is same as vol2 - valReal = 1./sqrt(mydot(self.n, self.n)) - self.n[0] *= valReal - self.n[1] *= valReal - elif dim == 3: - for j in range(dim): - self.x[0, j] = simplex2[1, j]-simplex2[0, j] - for j in range(dim): - self.x[1, j] = simplex2[2, j]-simplex2[0, j] - self.n[0] = self.x[0, 1]*self.x[1, 2]-self.x[0, 2]*self.x[1, 1] - self.n[1] = self.x[0, 2]*self.x[1, 0]-self.x[0, 0]*self.x[1, 2] - self.n[2] = self.x[0, 0]*self.x[1, 1]-self.x[0, 1]*self.x[1, 0] - valReal = 1./sqrt(mydot(self.n, self.n)) - self.n[0] *= valReal - self.n[1] *= valReal - self.n[2] *= valReal + self.kernel.setSimplices(simplex1, simplex2) contrib[:] = 0. @@ -1080,21 +1485,14 @@ cdef class {SCALAR_label}nonlocalOperator({SCALAR_label}double_local_matrix_t): for k in range(qr2.rule1.num_nodes): for m in range(qr2.rule2.num_nodes): - if dim == 1: - nw = 1. - else: - normW = 0. - for j in range(dim): - self.w[j] = self.y[m, j]-self.x[k, j] - normW += self.w[j]**2 - normW = 1./sqrt(normW) - for j in range(dim): - self.w[j] *= normW - nw = mydot(self.n, self.w) i = k*qr2.rule2.num_nodes+m self.kernel.evalPtr(dim, &self.x[k, 0], &self.y[m, 0], &self.vec[0]) + if self.multiLogKernel: + fac = self.scaling_values[self.termNo] + for l in range(valueSize): + self.vec[l] *= fac for l in range(valueSize): - self.temp[i, l] = qr2.weights[i] * nw * self.vec[l] + self.temp[i, l] = qr2.weights[i] * self.vec[l] k = 0 for I in range(self.DoFMap.dofs_per_element): @@ -1106,3 +1504,84 @@ cdef class {SCALAR_label}nonlocalOperator({SCALAR_label}double_local_matrix_t): val += self.temp[i, m] * PHI[I, i] * PHI[J, i] contrib[k, m] = val*vol k += 1 + + cdef void eval_near_boundary(self, + {SCALAR}_t[:, ::1] contrib, + panelType panel, + MASK_t mask=ALL): + # Evaluates \\int_{K} u(x) v(x) \\int_{e} \\gamma(x, y) dy dx + cdef: + REAL_t vol1 = self.vol1, vol2 = self.vol2, vol + INDEX_t i, j, k, I, J, m, l + REAL_t[:, ::1] simplex1 = self.simplex1 + REAL_t[:, ::1] simplex2 = self.simplex2 + {SCALAR}_t val + singularityCancelationQuadRule qr + REAL_t[:, ::1] PHI + INDEX_t dofs_per_element = self.DoFMap.dofs_per_element + INDEX_t dim = self.DoFMap.mesh.dim + REAL_t x[3] + REAL_t y[3] + INDEX_t valueSize = self.kernel.valueSize + INDEX_t beta + REAL_t z, neg_log_z, neg_log_N, fac + + self.kernel.setSimplices(simplex1, simplex2) + + contrib[:] = 0. + + if panel == COMMON_FACE: + qr = self.qrFace + PHI = self.PHI_face2 + elif panel == COMMON_EDGE: + qr = self.qrEdge + PHI = self.PHI_edge2 + elif panel == COMMON_VERTEX: + qr = self.qrVertex + PHI = self.PHI_vertex2 + else: + raise NotImplementedError('Panel type unknown: {}.'.format(panel)) + + vol = vol1*vol2 + + for m in range(qr.num_nodes): + for j in range(dim): + x[j] = 0. + for k in range(self.manifold_dim1+1): + x[j] += simplex1[self.perm1[k], j]*qr.nodes[k, m] + y[j] = 0. + for k in range(self.manifold_dim2+1): + y[j] += simplex2[self.perm2[k], j]*qr.nodes[self.manifold_dim1+1+k, m] + self.kernel.evalPtr(dim, &x[0], &y[0], &self.vec[0]) + + if self.multiLogKernel: + # get non-vanishing part N of |x-y| + z = 0. + for j in range(dim): + z += (x[j]-y[j])**2 + neg_log_z = -0.5*log(z) + neg_log_N = neg_log_z+log(qr.singularPart[m]) + + fac = 0. + for beta in range(self.termNo, self.scaling_values.shape[0]): + fac += my_binom(beta, self.termNo) * self.scaling_values[beta] * neg_log_N**(beta-self.termNo) + fac *= neg_log_z**(-self.termNo) + for l in range(valueSize): + self.vec[l] *= fac + + for l in range(valueSize): + self.temp[m, l] = qr.weights[m] * self.vec[l] + for I in range(dofs_per_element): + i = self.perm[I] + for J in range(I, dofs_per_element): + j = self.perm[J] + if j < i: + k = dofs_per_element*j-(j*(j+1) >> 1) + i + else: + k = dofs_per_element*i-(i*(i+1) >> 1) + j + if mask[k]: + for l in range(valueSize): + val = 0. + for m in range(qr.num_nodes): + val += self.temp[m, l] * PHI[I, m] * PHI[J, m] + contrib[k, l] = val*vol diff --git a/nl/PyNucleus_nl/nonlocalProblems.py b/nl/PyNucleus_nl/nonlocalProblems.py index 66e720d1..b030a909 100644 --- a/nl/PyNucleus_nl/nonlocalProblems.py +++ b/nl/PyNucleus_nl/nonlocalProblems.py @@ -7,304 +7,48 @@ import numpy as np from PyNucleus_base import REAL -from PyNucleus_base.factory import factory + from PyNucleus_base.utilsFem import problem, generates -from PyNucleus_fem.mesh import (simpleInterval, intervalWithInteraction, - uniformSquare, squareWithInteractions, - discWithInteraction, - gradedDiscWithInteraction, - graded_interval, - double_graded_interval, - double_graded_interval_with_interaction, - discWithIslands, - twinDisc, - # box, - # boxWithInteractions, - ball) from PyNucleus_fem.functions import (Lambda, constant, indicatorFunctor, squareIndicator, radialIndicator, - solFractional1D, rhsFractional1D, - solFractional, rhsFractional2D, sqrtAffineFunction) -from PyNucleus_fem.DoFMaps import P1_DoFMap, str2DoFMapOrder -from PyNucleus_fem.mesh import meshFactory as meshFactoryClass -from PyNucleus_fem import (PHYSICAL, NO_BOUNDARY, +from . nonlocal_functions import (solFractional1D, rhsFractional1D, + solFractional, rhsFractional2D) +from . factories import rhsFractional2D_nonPeriodic +from PyNucleus_fem.DoFMaps import str2DoFMapOrder +from PyNucleus_fem import (NO_BOUNDARY, DIRICHLET, HOMOGENEOUS_DIRICHLET, NEUMANN, HOMOGENEOUS_NEUMANN, - NORM, dofmapFactory) -from PyNucleus_fem.factories import functionFactory, rhsFractional2D_nonPeriodic + dofmapFactory) +from PyNucleus_fem.factories import functionFactory from scipy.special import gamma as Gamma, binom from . twoPointFunctions import (constantTwoPoint, temperedTwoPoint, leftRightTwoPoint, interfaceTwoPoint, - smoothedLeftRightTwoPoint, - lambdaTwoPoint, - lookupTwoPoint) + smoothedLeftRightTwoPoint) from . interactionDomains import (fullSpace, - ball1_retriangulation, - ball1_barycenter, ball2_retriangulation, ball2_barycenter, - ballInf_retriangulation, - ballInf_barycenter, ellipse_retriangulation, ellipse_barycenter) from . fractionalOrders import (constFractionalOrder, variableConstFractionalOrder, constantNonSymFractionalOrder, leftRightFractionalOrder, - linearLeftRightFractionalOrder, smoothedLeftRightFractionalOrder, - innerOuterFractionalOrder, - smoothedInnerOuterFractionalOrder, - islandsFractionalOrder, layersFractionalOrder, singleVariableUnsymmetricFractionalOrder, feFractionalOrder) -from . kernelsCy import (getKernelEnum, - FRACTIONAL, INDICATOR, PERIDYNAMIC, GAUSSIAN, EXPONENTIAL, POLYNOMIAL, - LOGINVERSEDISTANCE, MONOMIAL, - ) -from . kernels import (getFractionalKernel, - getIntegrableKernel, - getKernel) +from . kernels import (getKernelEnum, + FRACTIONAL, INDICATOR, PERIDYNAMIC, GAUSSIAN, EXPONENTIAL) +from . factories import (nonlocalMeshFactory, + fractionalOrderFactory, + kernelFactory, + getKernel) from copy import deepcopy -class fractionalOrderFactoryClass(factory): - def build(self, name, *args, **kwargs): - dm = None - if 'dm' in kwargs: - dm = kwargs.pop('dm') - if dm is not None: - s = self.build(name, *args, **kwargs) - assert isinstance(s, (constFractionalOrder, variableConstFractionalOrder, - constantNonSymFractionalOrder, singleVariableUnsymmetricFractionalOrder)) - sVec = dm.interpolate(s.fixedY(np.zeros((dm.mesh.dim), dtype=REAL))) - return super().build('fe', sVec, s.min, s.max) - else: - return super().build(name, *args, **kwargs) - - -fractionalOrderFactory = fractionalOrderFactoryClass() -fractionalOrderFactory.register('constant', constFractionalOrder, aliases=['const']) -fractionalOrderFactory.register('varConst', variableConstFractionalOrder, aliases=['constVar', 'constantSym']) -fractionalOrderFactory.register('leftRight', leftRightFractionalOrder, aliases=['twoDomain']) -fractionalOrderFactory.register('linearLeftRightNonSym', linearLeftRightFractionalOrder) -fractionalOrderFactory.register('smoothedLeftRight', smoothedLeftRightFractionalOrder, params={'r': 0.1, 'slope': 200.}, aliases=['twoDomainNonSym']) -fractionalOrderFactory.register('constantNonSym', constantNonSymFractionalOrder) -fractionalOrderFactory.register('innerOuter', innerOuterFractionalOrder) -fractionalOrderFactory.register('innerOuterNonSym', smoothedInnerOuterFractionalOrder) -fractionalOrderFactory.register('islands', islandsFractionalOrder, params={'r': 0.1, 'r2': 0.6}) -fractionalOrderFactory.register('layers', layersFractionalOrder) -fractionalOrderFactory.register('fe', feFractionalOrder) - -twoPointFunctionFactory = factory() -twoPointFunctionFactory.register('constant', constantTwoPoint, aliases=['const', 'constantTwoPoint']) -twoPointFunctionFactory.register('tempered', temperedTwoPoint, aliases=['temperedTwoPoint']) -twoPointFunctionFactory.register('leftRight', leftRightTwoPoint, aliases=['leftRightTwoPoint']) -twoPointFunctionFactory.register('interface', interfaceTwoPoint, aliases=['interfaceTwoPoint']) -twoPointFunctionFactory.register('lambda', lambdaTwoPoint) -twoPointFunctionFactory.register('lookup', lookupTwoPoint) - -interactionFactory = factory() -interactionFactory.register('fullSpace', fullSpace, aliases=['full']) -interactionFactory.register('ball2_retriangulation', ball2_retriangulation, aliases=['ball2', '2', 2]) -interactionFactory.register('ball2_barycenter', ball2_barycenter) -interactionFactory.register('ball1_retriangulation', ball1_retriangulation, aliases=['ball1', '1', 1]) -interactionFactory.register('ball1_barycenter', ball1_barycenter) -interactionFactory.register('ballInf_retriangulation', ballInf_retriangulation, aliases=['ballInf', 'inf', np.inf]) -interactionFactory.register('ballInf_barycenter', ballInf_barycenter) -interactionFactory.register('ellipse_retriangulation', ellipse_retriangulation, aliases=['ellipse']) -interactionFactory.register('ellipse_barycenter', ellipse_barycenter) - -kernelFactory = factory() -kernelFactory.register('fractional', getFractionalKernel) -kernelFactory.register('indicator', getIntegrableKernel, params={'kernel': INDICATOR}, aliases=['constant']) -kernelFactory.register('inverseDistance', getIntegrableKernel, params={'kernel': PERIDYNAMIC}, aliases=['peridynamic', 'inverseOfDistance']) -kernelFactory.register('gaussian', getIntegrableKernel, params={'kernel': GAUSSIAN}) -kernelFactory.register('exponential', getIntegrableKernel, params={'kernel': EXPONENTIAL}) -kernelFactory.register('polynomial', getIntegrableKernel, params={'kernel': POLYNOMIAL}) -kernelFactory.register('logInverseDistance', getIntegrableKernel, params={'kernel': LOGINVERSEDISTANCE}) -kernelFactory.register('monomial', getIntegrableKernel, params={'kernel': MONOMIAL}) - - -class nonlocalMeshFactoryClass(factory): - def __init__(self): - super(nonlocalMeshFactoryClass, self).__init__() - self.nonOverlappingMeshFactory = meshFactoryClass() - self.overlappingMeshFactory = meshFactoryClass() - - def register(self, name, classTypeNoOverlap, classTypeOverlap, dim, indicators, paramsNoOverlap={}, paramsOverlap={}, aliases=[]): - if classTypeNoOverlap is not None: - self.nonOverlappingMeshFactory.register(name, classTypeNoOverlap, dim, paramsNoOverlap, aliases) - if classTypeOverlap is not None: - self.overlappingMeshFactory.register(name, classTypeOverlap, dim, paramsOverlap, aliases) - super(nonlocalMeshFactoryClass, self).register(name, indicators) - - def build(self, name, kernel, boundaryCondition, noRef=0, useMulti=False, **kwargs): - skipMesh = False - if 'skipMesh' in kwargs: - skipMesh = kwargs.pop('skipMesh') - - if kernel is None: - horizonValue = 0. - elif isinstance(kernel.horizon, constant): - horizonValue = kernel.horizon.value - else: - horizonValue = kernel.max_horizon - - domainIndicator, boundaryIndicator, interactionIndicator = super(nonlocalMeshFactoryClass, self).build(name, **kwargs) - - if boundaryCondition == HOMOGENEOUS_DIRICHLET: - if horizonValue == np.inf: - # if kernel.s.max < 0.5: - # tag = NO_BOUNDARY - # else: - # tag = PHYSICAL - tag = PHYSICAL - zeroExterior = True - else: - tag = domainIndicator - zeroExterior = False - hasInteractionDomain = 0 < horizonValue < np.inf - elif boundaryCondition == HOMOGENEOUS_NEUMANN: - tag = NO_BOUNDARY - zeroExterior = False - hasInteractionDomain = False - elif boundaryCondition == DIRICHLET: - if horizonValue == np.inf: - if kernel.s.max < 0.5: - tag = NO_BOUNDARY - else: - tag = PHYSICAL - raise NotImplementedError("Non-homogeneous Dirichlet conditions for infinite horizon kernels are not implemented.") - else: - tag = NO_BOUNDARY - zeroExterior = False - hasInteractionDomain = 0 < horizonValue < np.inf - elif boundaryCondition == NEUMANN: - if horizonValue == np.inf: - assert False - else: - tag = NO_BOUNDARY - zeroExterior = False - hasInteractionDomain = True - elif boundaryCondition == NORM: - tag = PHYSICAL - zeroExterior = kernel.s.max >= 0.5 - hasInteractionDomain = False - else: - raise NotImplementedError('Unknown boundary condition {}'.format(boundaryCondition)) - - if not skipMesh: - if hasInteractionDomain: - assert 0 < horizonValue < np.inf, horizonValue - kwargs['horizon'] = horizonValue - mesh = self.overlappingMeshFactory.build(name, noRef, **kwargs) - else: - mesh = self.nonOverlappingMeshFactory.build(name, noRef, **kwargs) - - dmTest = P1_DoFMap(mesh, tag) - while dmTest.num_dofs == 0: - mesh = mesh.refine() - dmTest = P1_DoFMap(mesh, tag) - - nonlocalInfo = {'domain': domainIndicator, - 'boundary': boundaryIndicator, - 'interaction': interactionIndicator, - 'tag': tag, - 'zeroExterior': zeroExterior} - if not skipMesh: - return mesh, nonlocalInfo - else: - return nonlocalInfo - - def getDim(self, name): - return self.nonOverlappingMeshFactory.getDim(name) - - -def intervalIndicators(a=-1, b=1, **kwargs): - eps = 1e-12 - domainIndicator = squareIndicator(np.array([a+eps], dtype=REAL), - np.array([b-eps], dtype=REAL)) - interactionIndicator = Lambda(lambda x: 1. if ((x[0] < a-eps) or (b+eps < x[0])) else 0.) - boundaryIndicator = Lambda(lambda x: 1. if ((a-eps < x[0] < a+eps) or (b-eps < x[0] < b+eps)) else 0.) - return domainIndicator, boundaryIndicator, interactionIndicator - - -def squareIndicators(ax=-1., ay=-1., bx=1., by=1., **kwargs): - eps = 1e-12 - domainIndicator = squareIndicator(np.array([ax+eps, ay+eps], dtype=REAL), - np.array([bx-eps, by-eps], dtype=REAL)) - interactionIndicator = constant(1.)-squareIndicator(np.array([ax-eps, ay-eps], dtype=REAL), - np.array([bx+eps, by+eps], dtype=REAL)) - boundaryIndicator = constant(1.)-domainIndicator-interactionIndicator - return domainIndicator, boundaryIndicator, interactionIndicator - - -def radialIndicators(*args, **kwargs): - eps = 1e-12 - domainIndicator = radialIndicator(1.-eps) - interactionIndicator = constant(1.)-radialIndicator(1.+eps) - boundaryIndicator = radialIndicator(1.+eps)-radialIndicator(1.-eps) - return domainIndicator, boundaryIndicator, interactionIndicator - - -def twinDiscIndicators(radius=1., sep=0.1, **kwargs): - eps = 1e-9 - domainIndicator = (radialIndicator(radius-eps, np.array([sep/2+radius, 0.], dtype=REAL)) + - radialIndicator(radius-eps, np.array([-sep/2-radius, 0.], dtype=REAL))) - interactionIndicator = constant(1.)-(radialIndicator(radius+eps, np.array([sep/2+radius, 0.], dtype=REAL)) + - radialIndicator(radius+eps, np.array([-sep/2-radius, 0.], dtype=REAL))) - boundaryIndicator = ((radialIndicator(radius+eps, np.array([sep/2+radius, 0.], dtype=REAL)) + - radialIndicator(radius+eps, np.array([-sep/2-radius, 0.], dtype=REAL))) - - (radialIndicator(radius-eps, np.array([sep/2+radius, 0.], dtype=REAL)) + - radialIndicator(radius-eps, np.array([-sep/2-radius, 0.], dtype=REAL)))) - return domainIndicator, boundaryIndicator, interactionIndicator - - -def boxIndicators(ax=-1., ay=-1., az=-1., bx=1., by=1., bz=1., **kwargs): - eps = 1e-9 - domainIndicator = squareIndicator(np.array([ax+eps, ay+eps, az+eps], dtype=REAL), - np.array([bx-eps, by-eps, bz-eps], dtype=REAL)) - interactionIndicator = constant(1.)-squareIndicator(np.array([ax-eps, ay-eps, az-eps], dtype=REAL), - np.array([bx+eps, by+eps, bz+eps], dtype=REAL)) - boundaryIndicator = constant(1.)-domainIndicator-interactionIndicator - return domainIndicator, boundaryIndicator, interactionIndicator - - -def ballWithInteractions(*args, **kwargs): - radius = kwargs.get('radius') - horizon = kwargs.get('horizon') - kwargs['radius'] = radius+horizon - return ball(**kwargs) - - -nonlocalMeshFactory = nonlocalMeshFactoryClass() -nonlocalMeshFactory.register('interval', simpleInterval, intervalWithInteraction, 1, intervalIndicators, - {'a': -1, 'b': 1}, {'a': -1, 'b': 1}) -nonlocalMeshFactory.register('gradedInterval', graded_interval, double_graded_interval_with_interaction, 1, intervalIndicators, - {'a': -1, 'b': 1, 'mu': 2., 'mu2': 2.}, {'a': -1, 'b': 1, 'mu_ll': 2., 'mu_rr': 2.}) -nonlocalMeshFactory.register('square', uniformSquare, squareWithInteractions, 2, squareIndicators, - {'N': 2, 'M': 2, 'ax': -1, 'ay': -1, 'bx': 1, 'by': 1}, {'ax': -1, 'ay': -1, 'bx': 1, 'by': 1}, aliases=['rectangle']) -nonlocalMeshFactory.register('disc', discWithInteraction, discWithInteraction, 2, radialIndicators, - {'horizon': 0., 'radius': 1.}, {'radius': 1.}) -nonlocalMeshFactory.register('gradedDisc', gradedDiscWithInteraction, gradedDiscWithInteraction, 2, radialIndicators, - {'horizon': 0., 'radius': 1.}, {'radius': 1.}) -nonlocalMeshFactory.register('discWithIslands', discWithIslands, discWithIslands, 2, radialIndicators, - {'horizon': 0., 'radius': 1., 'islandOffCenter': 0.35, 'islandDiam': 0.5}, - {'radius': 1., 'islandOffCenter': 0.35, 'islandDiam': 0.5}) -nonlocalMeshFactory.register('twinDisc', twinDisc, twinDisc, 2, radialIndicators, - {'radius': 1., 'sep': 0.1}, {'radius': 1., 'sep': 0.1}) -# nonlocalMeshFactory.register('box', box, boxWithInteractions, 3, boxIndicators, -# {'Nx': 2, 'Ny': 2, 'Nz': 2, 'ax': -1, 'ay': -1, 'az': -1, 'bx': 1, 'by': 1, 'bz': 1}, -# {'Nx': 2, 'Ny': 2, 'Nz': 2, 'ax': -1, 'ay': -1, 'az': -1, 'bx': 1, 'by': 1, 'bz': 1}) -nonlocalMeshFactory.register('ball', ball, ballWithInteractions, 3, radialIndicators, - {'radius': 1.}, {'radius': 1.}) - - class nonlocalBaseProblem(problem): def __init__(self, driver): super().__init__(driver) @@ -348,6 +92,8 @@ def setDriverArgs(self): self.setDriverFlag('discretizedOrder', False, help='Use a FE function for the fractional order s.', group=p) self.setDriverFlag('gaussianVariance', 1.0, help='Variance of Gaussian kernel with infinite horizon.', group=p) self.setDriverFlag('exponentialRate', 1.0, help='Parameter of exponential kernel.', group=p) + self.setDriverFlag('interpolationMaxDist', 0., group=p) + self.setDriverFlag('interpolationNumNodes', 0, group=p) def processCmdline(self, params): dim = nonlocalMeshFactory.getDim(params['domain']) @@ -416,7 +162,8 @@ def constructAuxiliarySpace(self): @generates(['kernel', 'rangedKernel']) def processKernel(self, dim, kernelType, sType, sArgs, phiType, phiArgs, horizon, interaction, normalized, admissibleParams, - discretizedOrder, dmAux, feOrder, gaussianVariance, exponentialRate): + discretizedOrder, dmAux, feOrder, gaussianVariance, exponentialRate, + interpolationNumNodes, interpolationMaxDist): if kernelType == 'local': self.kernel = None @@ -427,7 +174,7 @@ def processKernel(self, dim, kernelType, sType, sArgs, phiType, phiArgs, horizon if admissibleParams is not None: assert kType == FRACTIONAL assert sType == 'const' - from PyNucleus_nl.kernelsCy import RangedFractionalKernel + from PyNucleus_nl.kernels import RangedFractionalKernel rangedKernel = self.directlyGetWithoutChecks('rangedKernel') if rangedKernel is None or not isinstance(rangedKernel, RangedFractionalKernel): self.rangedKernel = RangedFractionalKernel(dim, @@ -514,18 +261,39 @@ def processKernel(self, dim, kernelType, sType, sArgs, phiType, phiArgs, horizon if sFun is not None: piecewise &= sFun.symmetric piecewise &= isinstance(horizonFun, constant) - self.kernel = getKernel(dim=dim, kernel=kType, s=sFun, horizon=horizonFun, normalized=normalized, phi=phiFun, - interaction=interactionFun, piecewise=piecewise, - max_horizon=max_horizon, - variance=gaussianVariance, - exponentialRate=exponentialRate) + kernel = getKernel(dim=dim, kernel=kType, s=sFun, horizon=horizonFun, normalized=normalized, phi=phiFun, + interaction=interactionFun, piecewise=piecewise, + max_horizon=max_horizon, + variance=gaussianVariance, + exponentialRate=exponentialRate) + + if interpolationNumNodes > 0 and interpolationMaxDist > 0.: + if kernel.singularityValue < 0.: + interpolation_nodes = np.linspace(0., interpolationMaxDist, interpolationNumNodes+1)[1:] + else: + interpolation_nodes = np.linspace(0., interpolationMaxDist, interpolationNumNodes) + kernel = kernel.interpolate(interpolation_nodes) + self.kernel = kernel def report(self, group): + from . kernels import Kernel, FractionalKernel, MultiSingularityKernel, MultiSingularityFractionalKernel group.add('kernel', self.kernel) if self.kernel is not None: - if self.kernel.kernelType == FRACTIONAL: + group.add('kernel expression', self.kernel.getLongDescription()) + group.add('boundary kernel expression', self.kernel.getBoundaryKernel().getLongDescription()) + if isinstance(self.kernel, FractionalKernel): group.add('s', self.kernel.s) + if isinstance(self.kernel, MultiSingularityFractionalKernel): + group.add('s', self.kernel.kernels[0].s) + if isinstance(self.kernel, Kernel): + group.add('interaction', self.kernel.interaction) + if isinstance(self.kernel, MultiSingularityKernel): + group.add('interaction', self.kernel.kernels[0].interaction) group.add('horizon', self.horizon) + if hasattr(self.kernel, 'phi') and self.kernel.phi is not None: + group.add('phi', self.kernel.phi) + group.add('problem', self.problemDescription) + group.add('has analytic solution', self.analyticSolution is not None) class fractionalLaplacianProblem(nonlocalBaseProblem): @@ -692,13 +460,13 @@ def processProblem(self, kernel, dim, domain, domainParams, problem, normalized) def fun(x): kernel.evalParams_py(x, x) sVal = kernel.sValue - fac = 2*kernel.scalingValue + fac = kernel.scalingValue return fac/(2*sVal-1) * ((1-x[0])**(1-2*sVal) - (1+x[0])**(1-2*sVal)) else: sVal = s.value - fac = 2*kernel.scalingValue + fac = kernel.scalingValue assert sVal != 0.5 def fun(x): @@ -709,21 +477,10 @@ def fun(x): L2_ex = np.sqrt(2/3) elif problem == 'knownSolution': self.problemDescription = "Known analytic solution for variable fractional order, homogeneous Dirichlet volume condition" - from scipy.special import hyp2f1 - assert isinstance(s, (constFractionalOrder, variableConstFractionalOrder, - constantNonSymFractionalOrder, singleVariableUnsymmetricFractionalOrder)), s - beta = 0.7 - - def fun(x): - kernel.evalParams_py(x, x) - sVal = kernel.sValue - - return 2**(2*sVal) * Gamma(sVal+0.5)*Gamma(beta+1.)/np.sqrt(np.pi)/Gamma(beta+1.-sVal) * hyp2f1(sVal+0.5, -beta+sVal, 0.5, x[0]**2) - - self.rhs = functionFactory('Lambda', fun) - self.analyticSolution = functionFactory('Lambda', lambda x: (1.-x[0]**2)**beta) - L2_ex = np.sqrt(np.sqrt(np.pi) * Gamma(1+2*beta)/Gamma(3/2+2*beta) * radius**2) + self.rhs = functionFactory('rhsArbitraryOrder', dim, beta, s) + self.analyticSolution = functionFactory('solutionArbitraryOrder', dim, beta) + L2_ex = self.analyticSolution.L2norm elif problem == 'Greens': self.problemDescription = "Narrow indicator function forcing, homogeneous Neumann volume condition" boundaryCondition = HOMOGENEOUS_NEUMANN @@ -782,21 +539,10 @@ def fun(x): self.rhs = Lambda(lambda x: np.sin(np.pi*(x[0]**2+x[1]**2))) elif problem == 'knownSolution': self.problemDescription = "Known analytic solution for variable fractional order, homogeneous Dirichlet volume condition" - from scipy.special import hyp2f1 - assert isinstance(s, (constFractionalOrder, variableConstFractionalOrder, - constantNonSymFractionalOrder, singleVariableUnsymmetricFractionalOrder)), s - beta = 0.7 - - def fun(x): - kernel.evalParams_py(x, x) - sVal = kernel.sValue - - return 2**(2*sVal) * Gamma(sVal+1.0)*Gamma(beta+1.)/Gamma(beta+1.-sVal) * hyp2f1(sVal+1.0, -beta+sVal, 1.0, np.linalg.norm(x)**2) - - self.rhs = functionFactory('Lambda', fun) - self.analyticSolution = functionFactory('Lambda', lambda x: max(1.-np.linalg.norm(x)**2, 0.)**beta) - L2_ex = np.sqrt(np.pi/(1+2*beta)*radius**2) + self.rhs = functionFactory('rhsArbitraryOrder', dim, beta, s) + self.analyticSolution = functionFactory('solutionArbitraryOrder', dim, beta) + L2_ex = self.analyticSolution.L2norm else: raise NotImplementedError(problem) elif domain == 'square': @@ -913,19 +659,21 @@ def constructAuxiliarySpace(self, dim, domain, domainParams, kernelType, horizon class nonlocalPoissonProblem(nonlocalBaseProblem): def setDriverArgs(self): super().setDriverArgs() - self.setDriverFlag('domain', 'interval', acceptedValues=['gradedInterval', 'square', 'disc', 'gradedDisc', 'discWithIslands'], help='spatial domain') + self.setDriverFlag('domain', 'interval', acceptedValues=['gradedInterval', 'square', 'disc', 'gradedDisc', 'discWithIslands', 'ball'], help='spatial domain') self.addParametrizedArg('indicator', [float, float]) self.addParametrizedArg('polynomial', [int]) self.addParametrizedArg('linear1d', [float]) self.addParametrizedArg('quadratic1d', [float, float]) + self.addParametrizedArg('constant2d', [float]) self.addParametrizedArg('linear2d', [float, float]) self.addParametrizedArg('quadratic2d', [float, float, float]) self.setDriverFlag('problem', 'poly-Dirichlet', - argInterpreter=self.argInterpreter(['indicator', 'polynomial', 'linear1d', 'quadratic1d', 'linear2d', 'quadratic2d'], + argInterpreter=self.argInterpreter(['indicator', 'polynomial', 'linear1d', 'quadratic1d', 'constant2d', 'linear2d', 'quadratic2d'], acceptedValues=['poly-Dirichlet', 'poly-Neumann', 'zeroFlux', 'source', 'constant', 'gaussian', 'exponential', 'exact-sin-Dirichlet', 'exact-sin-Neumann', 'sin-Dirichlet', 'discontinuous']), help="select a problem to solve") + self.setDriverFlag('boundaryConditionType', acceptedValues=['default', 'dirichlet', 'neumann']) self.setDriverFlag('hTarget', argInterpreter=float, help="mesh size of initial mesh") self.setDriverFlag('noRef', argInterpreter=int, help="number of uniform mesh refinements applied to initial mesh") self.setDriverFlag('element', acceptedValues=['P1', 'P0', 'P2'], help="finite element space") @@ -943,6 +691,8 @@ def processCmdline(self, params): noRef = 4 elif domain == 'discWithIslands': noRef = 4 + elif domain == 'ball': + noRef = 1 else: raise NotImplementedError(domain) params['noRef'] = noRef @@ -954,7 +704,7 @@ def processCmdline(self, params): 'rhs', 'rhsData', 'dirichletData', 'fluxData', 'analyticSolution', 'exactL2Squared', 'exactHsSquared', 'problemDescription']) - def processProblem(self, kernel, domain, problem, normalized): + def processProblem(self, kernel, domain, problem, normalized, boundaryConditionType): if kernel is not None: kType = kernel.kernelType phiFun = kernel.phi @@ -974,14 +724,21 @@ def processProblem(self, kernel, domain, problem, normalized): self.problemDescription = "" - if problem in ('poly-Neumann', 'exact-sin-Neumann', 'zeroFlux'): + if boundaryConditionType.upper() == 'DEFAULT': + if problem in ('poly-Neumann', 'exact-sin-Neumann', 'zeroFlux'): + self.boundaryCondition = NEUMANN + elif self.parametrizedArg('indicator').match(problem): + self.boundaryCondition = HOMOGENEOUS_DIRICHLET + elif problem in ('source', 'constant', 'gaussian', 'exponential'): + self.boundaryCondition = HOMOGENEOUS_DIRICHLET + else: + self.boundaryCondition = DIRICHLET + elif boundaryConditionType.upper() == 'DIRICHLET': + self.boundaryCondition = DIRICHLET + elif boundaryConditionType.upper() == 'NEUMANN': self.boundaryCondition = NEUMANN - elif self.parametrizedArg('indicator').match(problem): - self.boundaryCondition = HOMOGENEOUS_DIRICHLET - elif problem in ('source', 'constant', 'gaussian', 'exponential'): - self.boundaryCondition = HOMOGENEOUS_DIRICHLET else: - self.boundaryCondition = DIRICHLET + raise NotImplementedError(boundaryConditionType) mesh_params = {'kernel': kernel, 'boundaryCondition': self.boundaryCondition} if domain in ('interval', 'gradedInterval'): @@ -1043,7 +800,12 @@ def processProblem(self, kernel, domain, problem, normalized): elif (self.parametrizedArg('linear1d').match(problem) or self.parametrizedArg('quadratic1d').match(problem)): - self.domainIndicator = domainIndicator + if self.boundaryCondition == DIRICHLET: + self.domainIndicator = domainIndicator + self.fluxIndicator = constant(0) + else: + self.domainIndicator = domainIndicator + self.fluxIndicator = interactionIndicator+boundaryIndicator # u(x) = x \dot (Bx) + c \dot x + d @@ -1071,7 +833,7 @@ def processProblem(self, kernel, domain, problem, normalized): b = kernel.horizon.c self.fluxIndicator = constant(0) - C = 2.*kernel.scalingValue + C = kernel.scalingValue trB = B[0, 0] @@ -1082,9 +844,9 @@ def delta_tilde_x_squared(x): return 2*np.vdot(a, x) + b + np.vdot(a, a) if isinstance(kernel.interaction, ball2_retriangulation): - self.rhsData = -C*2. * functionFactory('Lambda', - lambda x: trB/3. * (delta_x_squared(x)**(3/2) + delta_tilde_x_squared(x)**(3/2)) - + np.vdot(c+2*B@x+B@a, a) * delta_tilde_x_squared(x)**(1/2)) + self.rhsData = -C*2. * functionFactory('Lambda', + lambda x: trB/3. * (delta_x_squared(x)**(3/2) + delta_tilde_x_squared(x)**(3/2)) + + np.vdot(c+2*B@x+B@a, a) * delta_tilde_x_squared(x)**(1/2)) elif isinstance(kernel.interaction, ball2_dilation_retriangulation): self.rhsData = -C * functionFactory('Lambda', lambda x: trB/3. * (delta_x_squared(x)**(3/2) + (delta_tilde_x_squared(x)**(1/2)-a[0])**3) @@ -1095,7 +857,7 @@ def delta_tilde_x_squared(x): self.fluxData = constant(0) self.dirichletData = functionFactory('Lambda', lambda x: np.vdot(x, B@x) + np.vdot(c, x) + d) - if (kType == INDICATOR) and (phiFun is None) and (not normalized) and isinstance(interactionFun, ball2_retriangulation): + if (kType == INDICATOR) and (phiFun is None) and (not normalized) and isinstance(interactionFun, ball2_retriangulation) and self.boundaryCondition == DIRICHLET: self.analyticSolution = self.dirichletData else: raise NotImplementedError() @@ -1110,7 +872,7 @@ def delta_tilde_x_squared(x): sin = functionFactory('sin1d') if kType == INDICATOR: - self.rhsData = -2.*scalingValue * 2*(np.sin(np.pi*horizonValue)/np.pi-horizonValue) * sin + self.rhsData = -scalingValue * 2*(np.sin(np.pi*horizonValue)/np.pi-horizonValue) * sin elif kType == FRACTIONAL: from scipy.integrate import quad assert isinstance(sFun, constFractionalOrder) @@ -1128,7 +890,7 @@ def Phi(delta): return 0. Phi_delta = Phi(horizonValue) - self.rhsData = 4 * scalingValue * Phi_delta * sin + self.rhsData = 2 * scalingValue * Phi_delta * sin self.fluxData = constant(0) self.dirichletData = sin self.analyticSolution = sin @@ -1161,15 +923,15 @@ def Phi(delta): Psi = lambda delta_min, delta_max: quad(lambda y: np.sin(np.pi*y)/y**(1+2*sBase), delta_min, delta_max)[0] Phi_delta = Phi(horizonValue) - self.rhsData = 4 * scalingValue * Phi_delta * sin + self.rhsData = 2 * scalingValue * Phi_delta * sin def fluxFun(x): dist = 1+horizonValue-abs(x[0]) assert dist >= 0 if x[0] > 0: - return 2 * scalingValue * ((Phi_delta + Phi(dist)) * sin(x) + (Psi(dist, horizonValue)) * cos(x)) + return scalingValue * ((Phi_delta + Phi(dist)) * sin(x) + (Psi(dist, horizonValue)) * cos(x)) else: - return 2 * scalingValue * ((Phi_delta + Phi(dist)) * sin(x) - (Psi(dist, horizonValue)) * cos(x)) + return scalingValue * ((Phi_delta + Phi(dist)) * sin(x) - (Psi(dist, horizonValue)) * cos(x)) self.fluxData = Lambda(fluxFun) self.dirichletData = sin @@ -1205,18 +967,18 @@ def fluxFun(x): # return (1+(dist/horizonBase)**(2-2*sBase) - 2*abs(x[0]) * (2-2*sBase)/(1-2*sBase)/horizonBase * (1-(dist/horizonBase)**(1-2*sBase))) dist = 1+horizonBase-abs(x[0]) assert dist >= 0 - return 2*self.kernel.scalingValue * ((2*abs(x[0])/(1-2*sBase)) * (dist**(1-2*sBase)-horizonBase**(1-2*sBase)) + - 1/(2-2*sBase) * (dist**(2-2*sBase)+horizonBase**(2-2*sBase))) + return self.kernel.scalingValue * ((2*abs(x[0])/(1-2*sBase)) * (dist**(1-2*sBase)-horizonBase**(1-2*sBase)) + + 1/(2-2*sBase) * (dist**(2-2*sBase)+horizonBase**(2-2*sBase))) elif kType == PERIDYNAMIC: def fluxFun(x): dist = 1+horizonBase-abs(x[0]) assert dist >= 0 - return 2*self.kernel.scalingValue * (2*abs(x[0]) * (1-abs(x[0])) + 0.5 * (dist**2+horizonBase**2)) + return self.kernel.scalingValue * (2*abs(x[0]) * (1-abs(x[0])) + 0.5 * (dist**2+horizonBase**2)) elif kType == INDICATOR: def fluxFun(x): dist = 1+horizonBase-abs(x[0]) assert dist >= 0 - return 2*self.kernel.scalingValue * (abs(x[0]) * (dist**2-horizonBase**2) + 1./3. * (dist**3+horizonBase**3)) + return self.kernel.scalingValue * (abs(x[0]) * (dist**2-horizonBase**2) + 1./3. * (dist**3+horizonBase**3)) self.rhsData = constant(2) self.fluxData = Lambda(fluxFun) @@ -1261,7 +1023,7 @@ def fluxFun(x): self.interactionIndicator = interactionIndicator+boundaryIndicator self.rhsData = functionFactory('Lambda', lambda x: np.exp(-0.5*x[0]**2/gaussian_variance) - -np.exp(-0.25*x[0]**2/gaussian_variance)/np.sqrt(2)) + - np.exp(-0.25*x[0]**2/gaussian_variance)/np.sqrt(2)) self.fluxData = constant(0) self.dirichletData = constant(0.) if (kType == GAUSSIAN) and not self.kernel.finiteHorizon: @@ -1277,7 +1039,7 @@ def fluxFun(x): self.domainIndicator = domainIndicator self.fluxIndicator = constant(0) self.interactionIndicator = interactionIndicator+boundaryIndicator - self.rhsData = functionFactory('Lambda', lambda x: np.exp(-exponentialRate*abs(x[0])) * (1/exponentialRate-abs(x[0])) * kernel.scalingValue * 2.0) + self.rhsData = functionFactory('Lambda', lambda x: np.exp(-exponentialRate*abs(x[0])) * (1/exponentialRate-abs(x[0])) * kernel.scalingValue) self.fluxData = constant(0) self.dirichletData = constant(0.) if (kType == EXPONENTIAL) and not self.kernel.finiteHorizon: @@ -1343,13 +1105,30 @@ def f1lam(x): phiFun is None and normalized): self.analyticSolution = Lambda(lambda x: 1-x[0]**2) - elif (self.parametrizedArg('linear2d').match(problem) or + elif (self.parametrizedArg('constant2d').match(problem) or + self.parametrizedArg('linear2d').match(problem) or self.parametrizedArg('quadratic2d').match(problem)): + if self.boundaryCondition == DIRICHLET: + self.domainIndicator = domainIndicator + self.fluxIndicator = constant(0) + elif self.boundaryCondition == NEUMANN: + self.domainIndicator = domainIndicator + self.fluxIndicator = interactionIndicator+boundaryIndicator + else: + raise NotImplementedError() + # u(x) = x \dot (Bx) + c \dot x + d - if self.parametrizedArg('linear2d').match(problem): - self.problemDescription = "rhs giving rise to quadratic solution" + if self.parametrizedArg('constant2d').match(problem): + self.problemDescription = "rhs giving rise to constant solution" + + B = np.zeros((2, 2), dtype=REAL) + c = np.zeros((2), dtype=REAL) + d, = self.parametrizedArg('constant2d').interpret(problem) + + elif self.parametrizedArg('linear2d').match(problem): + self.problemDescription = "rhs giving rise to linear solution" B = np.zeros((2, 2), dtype=REAL) c = np.zeros((2), dtype=REAL) @@ -1369,7 +1148,7 @@ def f1lam(x): if isinstance(self.kernel.interaction.a, constant) and isinstance(self.kernel.interaction.b, constant) and isinstance(self.kernel.interaction.theta, constant): horizon = self.kernel.horizonValue - C = 2.*kernel.scalingValue + C = kernel.scalingValue theta = self.kernel.interaction.theta.value a = self.kernel.interaction.a.value @@ -1388,7 +1167,7 @@ def f1lam(x): def fun(x): horizon = self.kernel.horizonValue - C = 2.*kernel.scalingValue + C = kernel.scalingValue theta = self.kernel.interaction.theta(x) a = self.kernel.interaction.a(x) @@ -1406,20 +1185,41 @@ def fun(x): self.rhsData = functionFactory('Lambda', fun) + self.fluxData = constant(0) + self.dirichletData = functionFactory('Lambda', lambda x: np.vdot(x, B@x) + np.vdot(c, x) + d) + if (kType == INDICATOR) and (phiFun is None) and (self.boundaryCondition == DIRICHLET): + self.analyticSolution = self.dirichletData + elif isinstance(interactionFun, (ball2_barycenter, ball2_retriangulation)) and isinstance(kernel.horizon, sqrtAffineFunction): + # \delta = \sqrt{2 a \cdot x + b} + # \tilde{\delta} = \sqrt{2 a \cdot x + b + a \cdot a} + + a = 0.5*np.array(kernel.horizon.w) + b = kernel.horizon.c + self.fluxIndicator = constant(0) + C = kernel.scalingValue + trB = B[0, 0] + B[1, 1] + + def delta_x_squared(x): + return 2*np.vdot(a, x) + b + + def delta_tilde_x_squared(x): + return 2*np.vdot(a, x) + b + np.vdot(a, a) + self.rhsData = -C*np.pi * functionFactory('Lambda', + lambda x: 0.25*trB * (delta_x_squared(x)**2 + delta_tilde_x_squared(x)**2) + + np.vdot(c+2*B@x+B@a, a) * delta_tilde_x_squared(x)) self.fluxData = constant(0) self.dirichletData = functionFactory('Lambda', lambda x: np.vdot(x, B@x) + np.vdot(c, x) + d) - if (phiFun is None and normalized): + if (kType == INDICATOR) and (phiFun is None) and (not normalized) and (self.boundaryCondition == DIRICHLET): self.analyticSolution = self.dirichletData - elif isinstance(kernel.horizon, sqrtAffineFunction): + elif isinstance(interactionFun, ball2_dilation_retriangulation): # \delta = \sqrt{2 a \cdot x + b} # \tilde{\delta} = \sqrt{2 a \cdot x + b + a \cdot a} a = 0.5*np.array(kernel.horizon.w) b = kernel.horizon.c - self.fluxIndicator = constant(0) C = 2.*kernel.scalingValue trB = B[0, 0] + B[1, 1] @@ -1434,7 +1234,7 @@ def delta_tilde_x_squared(x): + np.vdot(c+2*B@x+B@a, a) * delta_tilde_x_squared(x)) self.fluxData = constant(0) self.dirichletData = functionFactory('Lambda', lambda x: np.vdot(x, B@x) + np.vdot(c, x) + d) - if (kType == INDICATOR) and (phiFun is None) and (not normalized) and isinstance(interactionFun, ball2_retriangulation): + if (kType == INDICATOR) and (phiFun is None) and (not normalized) and (self.boundaryCondition == DIRICHLET): self.analyticSolution = self.dirichletData else: raise NotImplementedError() @@ -1574,6 +1374,27 @@ def delta_tilde_x_squared(x): self.dirichletData = constant(0) else: raise NotImplementedError(problem) + elif domain == 'ball': + mesh_domain = domain + nI = nonlocalMeshFactory.build(mesh_domain, skipMesh=True, **mesh_params) + self.tag = nI['tag'] + self.zeroExterior = nI['zeroExterior'] + self.domainInteriorIndicator = domainIndicator = nI['domain'] + self.boundaryIndicator = boundaryIndicator = nI['boundary'] + self.interactionInteriorIndicator = interactionIndicator = nI['interaction'] + self.domainIndicator = domainIndicator+boundaryIndicator + self.interactionIndicator = interactionIndicator + if problem == 'constant': + self.fluxIndicator = constant(0) + self.rhsData = constant(1.) + self.fluxData = constant(0) + self.dirichletData = constant(0) + if (kType == FRACTIONAL) and (isinstance(self.kernel.s, constFractionalOrder) or + isinstance(self.kernel.s, variableConstFractionalOrder) or + isinstance(self.kernel.s, constantNonSymFractionalOrder)): + self.analyticSolution = functionFactory('solFractional', dim=3, s=self.kernel.s.value) + else: + raise NotImplementedError(problem) else: raise NotImplementedError(domain) @@ -1841,11 +1662,11 @@ def processProblem(self, domain, problem, element, kernel1Type, kernel2Type, hor def flux_left_lam(x): dist = 1+horizon1-x[0] - return 2*scaling1 * ((x[0]-1) * (dist**2-horizon1**2) + 1/3 * (horizon1**3 + dist**3)) + return scaling1 * ((x[0]-1) * (dist**2-horizon1**2) + 1/3 * (horizon1**3 + dist**3)) def flux_right_lam(x): dist = x[0]-(1-horizon2) - return 2*scaling2 * ((x[0]-1) * (horizon2**2-dist**2) + 1/3 * (horizon2**3 + dist**3)) + return scaling2 * ((x[0]-1) * (horizon2**2-dist**2) + 1/3 * (horizon2**3 + dist**3)) flux_left = Lambda(flux_left_lam) flux_right = Lambda(flux_right_lam) @@ -1875,11 +1696,11 @@ def flux_right_lam(x): def flux_left_lam(x): dist = 1+horizon1-x[0] - return -2*kernel1.scalingValue * (x[0] * (dist**2-horizon1**2) + 1/3 * (horizon1**3 + dist**3)) + return -kernel1.scalingValue * (x[0] * (dist**2-horizon1**2) + 1/3 * (horizon1**3 + dist**3)) def flux_right_lam(x): dist = x[0]-1+horizon2 - return -2*kernel2.scalingValue * ((x[0]-1) * (horizon2**2-dist**2) + 1/3 * (horizon2**3 + dist**3)) + return -kernel2.scalingValue * ((x[0]-1) * (horizon2**2-dist**2) + 1/3 * (horizon2**3 + dist**3)) flux_left = Lambda(flux_left_lam) flux_right = Lambda(flux_right_lam) @@ -1920,11 +1741,10 @@ def flux_right_lam(x): from scipy.integrate import quad if kType1 == INDICATOR: - forcing_left = -coeff11*(2.*scaling1) * 2*(np.sin(np.pi*horizon1)/np.pi-horizon1) * sin + forcing_left = -coeff11*scaling1 * 2*(np.sin(np.pi*horizon1)/np.pi-horizon1) * sin elif kType1 == FRACTIONAL: assert isinstance(kernel1.s, constFractionalOrder) sBase1 = kernel1.s.value - from scipy.special import gamma def Phi1(delta): if delta > 0: @@ -1936,7 +1756,7 @@ def Phi1(delta): else: return 0. - forcing_left = 4 * scaling1 * Phi1(horizon1) * sin + forcing_left = 2 * scaling1 * Phi1(horizon1) * sin def flux_left_lam(x): # assert c < x[0] < c+horizon1 @@ -1944,15 +1764,15 @@ def flux_left_lam(x): u2x = sol_2(x) Int = 0. if x[0]-horizon1 < c-horizon2: - Int += 2. * quad(lambda y: (u1x-sol_1(np.array([y]))) * kernel1(x, np.array([y])), x[0]-horizon1, c-horizon2)[0] + Int += quad(lambda y: (u1x-sol_1(np.array([y]))) * kernel1(x, np.array([y])), x[0]-horizon1, c-horizon2)[0] if max(c-horizon2, x[0]-horizon1) < c: - Int += 2. * quad(lambda y: (u1x-sol_1(np.array([y]))) * kernel1(x, np.array([y])), max(c-horizon2, x[0]-horizon1), c)[0] + Int += quad(lambda y: (u1x-sol_1(np.array([y]))) * kernel1(x, np.array([y])), max(c-horizon2, x[0]-horizon1), c)[0] if max(c-horizon2, x[0]-horizon2) < c: - Int -= 2. * quad(lambda y: (u2x-sol_2(np.array([y]))) * kernel2(x, np.array([y])), max(c-horizon2, x[0]-horizon2), c)[0] + Int -= quad(lambda y: (u2x-sol_2(np.array([y]))) * kernel2(x, np.array([y])), max(c-horizon2, x[0]-horizon2), c)[0] return Int if kType2 == INDICATOR: - forcing_right = -coeff22*(2.*scaling2) * 2*(np.sin(np.pi*horizon2)/np.pi-horizon2) * (-sin) + forcing_right = -coeff22*scaling2 * 2*(np.sin(np.pi*horizon2)/np.pi-horizon2) * (-sin) elif kType2 == FRACTIONAL: assert isinstance(kernel2.s, constFractionalOrder) sBase2 = kernel2.s.value @@ -1967,7 +1787,7 @@ def Phi2(delta): else: return 0. - forcing_right = 4 * scaling2 * Phi2(horizon2) * (-sin) + forcing_right = 2 * scaling2 * Phi2(horizon2) * (-sin) def flux_right_lam(x): # assert c-horizon2 < x[0] < c @@ -1975,11 +1795,11 @@ def flux_right_lam(x): u2x = sol_2(x) Int = 0. if c+horizon1 < x[0]+horizon2: - Int += 2. * quad(lambda y: (u2x-sol_2(np.array([y]))) * kernel2(x, np.array([y])), c+horizon1, x[0]+horizon2)[0] + Int += quad(lambda y: (u2x-sol_2(np.array([y]))) * kernel2(x, np.array([y])), c+horizon1, x[0]+horizon2)[0] if c < min(c+horizon1, x[0]+horizon2): - Int += 2. * quad(lambda y: (u2x-sol_2(np.array([y]))) * kernel2(x, np.array([y])), c, min(c+horizon1, x[0]+horizon2))[0] + Int += quad(lambda y: (u2x-sol_2(np.array([y]))) * kernel2(x, np.array([y])), c, min(c+horizon1, x[0]+horizon2))[0] if c < min(c+horizon1, x[0]+horizon1): - Int -= 2. * quad(lambda y: (u1x-sol_1(np.array([y]))) * kernel1(x, np.array([y])), c, min(c+horizon1, x[0]+horizon1))[0] + Int -= quad(lambda y: (u1x-sol_1(np.array([y]))) * kernel1(x, np.array([y])), c, min(c+horizon1, x[0]+horizon1))[0] return Int flux_left = Lambda(flux_left_lam) @@ -2102,13 +1922,13 @@ def bnds(x1, x2, y1, horizon): def flux_left_lam(x): dist = 1+horizon1-x[0] - return 4*scaling1 * (-2/3*(x[0]-1) * (horizon1**2-dist**2)**(3/2) + + return 2*scaling1 * (-2/3*(x[0]-1) * (horizon1**2-dist**2)**(3/2) + 1/8 * (np.sqrt(horizon1**2 - dist**2) * dist * (2*dist**2 - horizon1**2)) + horizon1**4/8 * (np.arcsin(dist/horizon1)-np.arcsin(-1))) def flux_right_lam(x): dist = x[0]-(1-horizon2) - return 4*scaling2 * (-2/3*(x[0]-1) * (-1)*(horizon2**2-dist**2)**(3/2) + + return 2*scaling2 * (-2/3*(x[0]-1) * (-1)*(horizon2**2-dist**2)**(3/2) + 1/8 * (np.sqrt(horizon2**2 - dist**2) * dist * (2*dist**2 - horizon2**2)) + horizon2**4/8 * (np.arcsin(1)-np.arcsin(-dist/horizon2))) @@ -2238,7 +2058,7 @@ def int2_2(x, y): return J if kType1 == INDICATOR: - forcing_left = coeff11*(2.*scaling1) * (np.pi*horizon1**2 - 2*horizon1*jv(1., horizon1*np.pi)) * sin + forcing_left = coeff11*scaling1 * (np.pi*horizon1**2 - 2*horizon1*jv(1., horizon1*np.pi)) * sin def flux_left_lam(x): # x \in I^J_1 @@ -2249,16 +2069,16 @@ def flux_left_lam(x): Int = 0. if x[0]-horizon1 < cx-horizon2: # Omega^J_1 - Int += 2. * quad(lambda y: (u1x-sol_1(np.array([y, x[1]]))) * int2_1(x, y) * - kernel1(x, np.array([y, x[1]])), x[0]-horizon1, cx-horizon2)[0] + Int += quad(lambda y: (u1x-sol_1(np.array([y, x[1]]))) * int2_1(x, y) * + kernel1(x, np.array([y, x[1]])), x[0]-horizon1, cx-horizon2)[0] if max(cx-horizon2, x[0]-horizon1) < cx: # I^J_2 - Int += 2. * quad(lambda y: (u1x-sol_1(np.array([y, x[1]]))) * int2_1(x, y) * - kernel1(x, np.array([y, x[1]])), max(cx-horizon2, x[0]-horizon1), cx)[0] + Int += quad(lambda y: (u1x-sol_1(np.array([y, x[1]]))) * int2_1(x, y) * + kernel1(x, np.array([y, x[1]])), max(cx-horizon2, x[0]-horizon1), cx)[0] if max(cx-horizon2, x[0]-horizon2) < cx: # I^J_2 - Int -= 2. * quad(lambda y: (u2x-sol_2(np.array([y, x[1]]))) * int2_2(x, y) * - kernel2(x, np.array([y, x[1]])), max(cx-horizon2, x[0]-horizon2), cx)[0] + Int -= quad(lambda y: (u2x-sol_2(np.array([y, x[1]]))) * int2_2(x, y) * + kernel2(x, np.array([y, x[1]])), max(cx-horizon2, x[0]-horizon2), cx)[0] return Int elif kType1 == FRACTIONAL: @@ -2267,7 +2087,7 @@ def flux_left_lam(x): fac1 = nquad(lambda rho, theta: (1-np.cos(np.pi*rho*np.cos(theta))) * rho**(-1-2*sBase1), [(0, horizon1), (0, 2*np.pi)])[0] - forcing_left = 2*scaling1 * fac1 * sin + forcing_left = scaling1 * fac1 * sin def flux_left_lam(x): # x in IJ1 @@ -2288,26 +2108,25 @@ def flux_right_lam(x): Int = 0. if cx+horizon1 < x[0]+horizon2: # Omega^J_2 - Int += 2. * quad(lambda y: (u2x-sol_2(np.array([y, x[1]]))) * int2_2(x, y) * - kernel2(x, np.array([y, x[1]])), cx+horizon1, x[0]+horizon2)[0] + Int += quad(lambda y: (u2x-sol_2(np.array([y, x[1]]))) * int2_2(x, y) * + kernel2(x, np.array([y, x[1]])), cx+horizon1, x[0]+horizon2)[0] if cx < min(cx+horizon1, x[0]+horizon2): # I^J_1 - Int += 2. * quad(lambda y: (u2x-sol_2(np.array([y, x[1]]))) * int2_2(x, y) * - kernel2(x, np.array([y, x[1]])), cx, min(cx+horizon1, x[0]+horizon2))[0] + Int += quad(lambda y: (u2x-sol_2(np.array([y, x[1]]))) * int2_2(x, y) * + kernel2(x, np.array([y, x[1]])), cx, min(cx+horizon1, x[0]+horizon2))[0] if cx < min(cx+horizon1, x[0]+horizon1): # I^J_1 - Int -= 2. * quad(lambda y: (u1x-sol_1(np.array([y, x[1]]))) * int2_1(x, y) * - kernel1(x, np.array([y, x[1]])), cx, min(cx+horizon1, x[0]+horizon1))[0] + Int -= quad(lambda y: (u1x-sol_1(np.array([y, x[1]]))) * int2_1(x, y) * + kernel1(x, np.array([y, x[1]])), cx, min(cx+horizon1, x[0]+horizon1))[0] return Int elif kType2 == FRACTIONAL: assert isinstance(kernel2.s, constFractionalOrder) sBase2 = kernel2.s.value - from scipy.special import gamma fac2 = nquad(lambda rho, theta: (1-np.cos(np.pi*rho*np.cos(theta))) * rho**(-1-2*sBase2), [(0, horizon2), (0, 2*np.pi)])[0] - forcing_right = 2*scaling2 * fac2 * (-sin) + forcing_right = scaling2 * fac2 * (-sin) def flux_right_lam(x): # x in IJ2 @@ -2367,14 +2186,14 @@ def evalRHSFac(alpha, beta, horizon, s, N=100): elif kType1 == FRACTIONAL: assert isinstance(kernel1.s, constFractionalOrder) sBase1 = kernel1.s.value - forcing_left = 2*(2*scaling1*evalRHSFac(np.pi, 2.*np.pi, horizon1, sBase1))*sin2d + forcing_left = 2*(scaling1*evalRHSFac(np.pi, 2.*np.pi, horizon1, sBase1))*sin2d if kType2 == INDICATOR: sBase2 = -1. elif kType2 == FRACTIONAL: assert isinstance(kernel2.s, constFractionalOrder) sBase2 = kernel2.s.value - forcing_right = -(2*scaling2*evalRHSFac(np.pi, np.pi, horizon2, sBase2))*sin + forcing_right = -(scaling2*evalRHSFac(np.pi, np.pi, horizon2, sBase2))*sin from scipy.integrate import quad, nquad from . strongForm import getStrongIntegrand diff --git a/nl/PyNucleus_nl/nonlocal_functions.pyx b/nl/PyNucleus_nl/nonlocal_functions.pyx new file mode 100644 index 00000000..98a41717 --- /dev/null +++ b/nl/PyNucleus_nl/nonlocal_functions.pyx @@ -0,0 +1,466 @@ +################################################################################### +# Copyright 2021 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. # +# If you want to use this code, please refer to the README.rst and LICENSE files. # +################################################################################### + +from libc.math cimport (sin, cos, sinh, cosh, tanh, sqrt, atan2, + M_PI as pi, pow, exp, floor, log2, log) +from . zeta cimport zetaCy as zeta +from scipy.special.cython_special cimport psi as digamma +from scipy.special.cython_special cimport gamma as cgamma +from PyNucleus_base.myTypes cimport INDEX_t, REAL_t, BOOL_t +from PyNucleus_base.blas import uninitialized +from PyNucleus_fem.functions cimport function + + +cdef inline REAL_t polygamma(INDEX_t n, REAL_t d) noexcept: + return (-1.0)**(n+1) * cgamma(n+1.0) * zeta(n+1, d) + + +cdef class solFractional(function): + cdef: + public REAL_t s + public REAL_t fac + REAL_t radius2 + INDEX_t dim + public REAL_t L2norm + + def __init__(self, REAL_t s, INDEX_t dim, REAL_t radius=1.0): + function.__init__(self) + from scipy.special import gamma + self.s = s + self.dim = dim + self.radius2 = radius**2 + self.fac = self.radius2**s * 2.**(-2.*s)*gamma(dim/2.)/gamma((dim+2.*s)/2.)/gamma(1.+s) + if dim == 1: + vol = 2. + elif dim == 2: + vol = 2.*pi + elif dim == 3: + vol = 4.*pi + else: + raise NotImplemented(dim) + if dim == 1: + # orthogonality of Jacobi polynomials + P_ss0_P_ss0 = pow(2., 1+4*s) * gamma(1+2*s)**2 / gamma(1+4*s) / (1+4*s) / gamma(1.) + ip = P_ss0_P_ss0 + self.L2norm = self.fac * pow(vol, 0.5) * pow(radius, 0.5*dim) * sqrt(0.5 * ip) + elif dim == 2: + self.L2norm = self.fac * sqrt(vol/2./(1+2*s)) * radius + elif dim == 3: + P_ss1_P_ss1 = pow(2., 1+4*s) * gamma(1+2*s+1)**2 / gamma(1+4*s+1) / (1+4*s+2) / gamma(2.) + ip = pow(s+1., -2.) * P_ss1_P_ss1 + self.L2norm = self.fac * pow(vol, 0.5) * pow(radius, 0.5*dim) * sqrt(0.5 * ip) + else: + raise NotImplemented(dim) + + cdef inline REAL_t eval(self, REAL_t[::1] x): + cdef REAL_t r2 = 0. + cdef INDEX_t i + for i in range(self.dim): + r2 += x[i]**2 + if r2 <= self.radius2: + return self.fac*pow(1.-r2/self.radius2, self.s) + else: + return 0. + + +from scipy.special import eval_jacobi as jacobi + + +cdef class rhsFractional1D(function): + cdef: + public REAL_t s + REAL_t fac + public INDEX_t n + + def __init__(self, REAL_t s, INDEX_t n): + from scipy.special import gamma + function.__init__(self) + self.s = s + self.n = n + self.fac = 2.**(2.*s)*gamma(0.5+s+n)*gamma(1.+s+n)/gamma(1.+n)/gamma(0.5+n) + + cdef inline REAL_t eval(self, REAL_t[::1] x): + cdef REAL_t r2 = 0. + r2 = x[0]**2 + if r2 <= 1.: + return self.fac * jacobi(self.n, self.s, -0.5, 2.*r2-1.) + else: + return 0. + + +cdef class solFractional1D(function): + cdef: + public REAL_t s + public INDEX_t n + + def __init__(self, REAL_t s, INDEX_t n): + function.__init__(self) + self.s = s + self.n = n + + cdef inline REAL_t eval(self, REAL_t[::1] x): + cdef REAL_t r2 = 0. + r2 = x[0]**2 + if r2 <= 1.: + return (1.-r2)**self.s * jacobi(self.n, self.s, -0.5, 2.*r2-1.) + else: + return 0. + + +cdef class rhsFractional2D(function): + cdef: + public REAL_t s + public REAL_t angular_shift + public INDEX_t l + public INDEX_t n + REAL_t fac + + def __init__(self, REAL_t s, INDEX_t l, INDEX_t n, REAL_t angular_shift=0.): + function.__init__(self) + from scipy.special import gamma + self.s = s + self.l = l + self.n = n + self.angular_shift = angular_shift + self.fac = 2.**(2.*s)*gamma(1.+s+n)*gamma(1.+l+s+n)/gamma(1+n)/gamma(1.+l+n) + + cdef inline REAL_t eval(self, REAL_t[::1] x): + cdef REAL_t r2 = 0., theta = atan2(x[1], x[0]) + r2 = x[0]**2+x[1]**2 + if r2 <= 1.: + return self.fac*r2**(0.5*self.l)*cos(self.l*(theta+self.angular_shift))*jacobi(self.n, self.s, self.l, 2.*r2-1.) + else: + return 0. + + +cdef class solFractional2D(function): + cdef: + public REAL_t s + public REAL_t angular_shift + public INDEX_t l + public INDEX_t n + + def __init__(self, REAL_t s, INDEX_t l, INDEX_t n, REAL_t angular_shift=0.): + function.__init__(self) + self.s = s + self.l = l + self.n = n + self.angular_shift = angular_shift + + cdef inline REAL_t eval(self, REAL_t[::1] x): + cdef REAL_t r2 = 0., theta = atan2(x[1], x[0]) + r2 = x[0]**2+x[1]**2 + if r2 <= 1.: + return (1.-r2)**self.s*r2**(0.5*self.l)*cos(self.l*(theta+self.angular_shift))*jacobi(self.n, self.s, self.l, 2.*r2-1.) + else: + return 0. + + +cdef class rhsFractional2Dcombination(function): + cdef list functions + + def __init__(self, REAL_t s, params): + function.__init__(self) + self.functions = [rhsFractional2D(s, **p) for p in params] + + cdef inline REAL_t eval(self, REAL_t[::1] x): + cdef: + REAL_t val = 0. + INDEX_t i + function f + for i in range(len(self.functions)): + f = self.functions[i] + val += f.eval(x) + return val + + +cdef class solFractional2Dcombination(function): + cdef list functions + + def __init__(self, REAL_t s, params): + function.__init__(self) + self.functions = [solFractional2D(s, **p) for p in params] + + cdef inline REAL_t eval(self, REAL_t[::1] x): + cdef: + REAL_t val = 0. + INDEX_t i + function f + for i in range(len(self.functions)): + f = self.functions[i] + val += f.eval(x) + return val + + +cdef class rhsTestFractional_U(function): + cdef REAL_t t + cdef function sol + + def __init__(self, REAL_t s, INDEX_t dim, REAL_t t, REAL_t radius=1.0): + function.__init__(self) + self.sol = solFractional(s, dim, radius) + self.t = t + + cdef inline REAL_t eval(self, REAL_t[::1] x): + cdef REAL_t u = self.sol.eval(x) + return cos(self.t)*u + (cos(self.t)**2-sin(self.t)**2)*u**2 + sin(self.t) + + +cdef class rhsTestFractional_V(function): + cdef REAL_t t + cdef function sol + + def __init__(self, REAL_t s, INDEX_t dim, REAL_t t, REAL_t radius=1.0): + function.__init__(self) + self.sol = solFractional(s, dim, radius) + self.t = t + + cdef inline REAL_t eval(self, REAL_t[::1] x): + cdef REAL_t u = self.sol.eval(x) + return -sin(self.t)*u + (-cos(self.t)**2+sin(self.t)**2)*u**2 + cos(self.t) + + +cdef class rhsFractionalBrusselator_U(function): + cdef REAL_t t, B, Q, eta, radius2s + cdef function solU, solV + + def __init__(self, REAL_t s1, REAL_t s2, + REAL_t B, REAL_t Q, REAL_t eta, + INDEX_t dim, REAL_t t, REAL_t radius=1.0): + function.__init__(self) + self.solU = solFractional(s1, dim, radius) + self.solV = solFractional(s2, dim, radius) + self.B = B + self.Q = Q + self.eta = eta + self.t = t + self.radius2s = radius**(2.*s1) + + cdef inline REAL_t eval(self, REAL_t[::1] x): + cdef: + REAL_t u0 = self.solU.eval(x)*self.eta + REAL_t v0 = self.solV.eval(x)/self.eta + REAL_t s = sin(self.t) + REAL_t c = cos(2.*self.t) + REAL_t u = u0*s + REAL_t v = v0*c + return (cos(self.t)*u0) + s*self.eta/self.radius2s - ((self.B-1.)*u + self.Q**2*v + self.B/self.Q*u**2 + 2.*self.Q*u*v + u**2*v) + + +cdef class rhsFractionalBrusselator_V(function): + cdef REAL_t t, B, Q, eta, radius2s + cdef function solU, solV + + def __init__(self, REAL_t s1, REAL_t s2, + REAL_t B, REAL_t Q, REAL_t eta, + INDEX_t dim, REAL_t t, REAL_t radius=1.0): + function.__init__(self) + self.solU = solFractional(s1, dim, radius) + self.solV = solFractional(s2, dim, radius) + self.B = B + self.Q = Q + self.eta = eta + self.t = t + self.radius2s = radius**(2.*s2) + + cdef inline REAL_t eval(self, REAL_t[::1] x): + cdef: + REAL_t u0 = self.solU.eval(x)*self.eta + REAL_t v0 = self.solV.eval(x)/self.eta + REAL_t s = sin(self.t) + REAL_t c = cos(2.*self.t) + REAL_t u = u0*s + REAL_t v = v0*c + return self.eta**2*(-2.*sin(2.*self.t)*v0) + c/self.eta/self.radius2s + (self.B*u + self.Q**2*v + self.B/self.Q*u**2 + 2.*self.Q*u*v + u**2*v) + + +cdef class solFractionalDerivative(function): + cdef public REAL_t s + cdef REAL_t fac, fac2, fac3, radius2 + cdef INDEX_t dim + cdef INDEX_t derivative + + def __init__(self, REAL_t s, INDEX_t dim, REAL_t radius=1.0, INDEX_t derivative=1): + function.__init__(self) + from scipy.special import gamma + self.s = s + self.dim = dim + self.radius2 = radius**2 + self.fac = self.radius2**s * 2.**(-2.*s)*gamma(dim/2.)/gamma((dim+2.*s)/2.)/gamma(1.+s) + self.fac2 = log(0.25*self.radius2) - digamma(0.5*dim+s) - digamma(1+s) + self.fac3 = -polygamma(1, 0.5*dim+s)-polygamma(1, 1+s) + self.derivative = derivative + assert self.derivative in (0, 1, 2) + + cdef inline REAL_t eval(self, REAL_t[::1] x): + cdef REAL_t r2 = 0. + cdef INDEX_t i + for i in range(self.dim): + r2 += x[i]**2 + if r2 < 1.: + if self.derivative == 0: + return self.fac*pow(1.-r2/self.radius2, self.s) + elif self.derivative == 1: + return (self.fac2+log(1.-r2/self.radius2)) * self.fac*pow(1.-r2/self.radius2, self.s) + elif self.derivative == 2: + return ((self.fac2 + log(1.-r2/self.radius2))**2 + self.fac3) * self.fac*pow(1.-r2/self.radius2, self.s) + else: + return 0. + + +cdef class solutionArbitraryOrder(function): + """ + u(x) = (1-\\frac{|x|^2}{R^2})^{\\beta} + """ + cdef: + public INDEX_t dim + public REAL_t beta + public REAL_t radius2 + public INDEX_t derivative + public REAL_t L2norm + + def __init__(self, INDEX_t dim, REAL_t beta, REAL_t radius=1., INDEX_t derivative=0): + self.dim = dim + self.beta = beta + self.radius2 = radius**2 + if derivative == 0: + # volume of ball with size=radius + if self.dim == 1: + vol = 2.*radius + elif self.dim == 2: + vol = pi*self.radius2 + else: + raise NotImplementedError() + + self.L2norm = sqrt(vol*hyp2f1(0.5*dim, -2*beta, 0.5*dim+1, 1.)) + else: + self.L2norm = 0. + + cdef inline REAL_t eval(self, REAL_t[::1] x): + cdef: + REAL_t r2 = 0. + INDEX_t i + if self.derivative == 0: + for i in range(self.dim): + r2 += x[i]**2 + r2 /= self.radius2 + if r2 < 1.: + return pow(1.-r2, self.beta) + else: + return 0. + else: + return 0. + + +from scipy.special import hyp2f1 + +from . fractionalOrders cimport fractionalOrderBase +from . fractionalOrders import (constFractionalOrder, + variableConstFractionalOrder, + constantNonSymFractionalOrder, + singleVariableUnsymmetricFractionalOrder) +from scipy.special.cython_special cimport gamma as cgamma + + +cdef inline REAL_t gamma(REAL_t d) noexcept: + return cgamma(d) + + +cdef class rhsArbitraryOrder(function): + cdef: + INDEX_t dim + public REAL_t beta + public fractionalOrderBase sFun + public INDEX_t derivative + REAL_t eps + REAL_t[::1] dir, grad + public BOOL_t normalized + + def __init__(self, INDEX_t dim, REAL_t beta, fractionalOrderBase sFun, INDEX_t derivative=0, eps=1e-6, REAL_t[::1] dir=None, BOOL_t normalized=True): + self.dim = dim + self.beta = beta + assert isinstance(sFun, (constFractionalOrder, variableConstFractionalOrder, + constantNonSymFractionalOrder, singleVariableUnsymmetricFractionalOrder)) + self.sFun = sFun + self.derivative = derivative + self.eps = eps + self.dir = dir + self.normalized = normalized + assert derivative in (0, 1, 2), derivative + if derivative == 1: + assert self.dir.shape[0] == self.sFun.numParameters + self.grad = uninitialized(self.sFun.numParameters) + elif derivative == 2: + assert self.dir.shape[0] == self.sFun.numParameters**2 + self.grad = uninitialized(self.sFun.numParameters) + + cdef inline REAL_t eval(self, REAL_t[::1] x): + cdef: + REAL_t r2 = 0. + REAL_t s, s2, s3, val, val2, val3 + INDEX_t i, j + REAL_t dhyp_ds, d2hyp_ds2, C, dC_ds, d2C_ds2 + REAL_t df_ds, d2f_ds2, ds_dp, d2s_dp2 + + self.sFun.evalPtr(self.dim, &x[0], &x[0], &s) + + for i in range(x.shape[0]): + r2 += x[i]**2 + if self.derivative == 0: + if self.normalized: + C = 2**(2*s) * gamma(0.5*(self.dim+2*s)) * gamma(1.+self.beta) / gamma(0.5*self.dim) / gamma(self.beta-s+1.) + else: + C = gamma(1.+self.beta) / gamma(0.5*self.dim) / gamma(self.beta-s+1.) / (s * pow(pi, -0.5*self.dim) / gamma(1.0-s)) + return C * hyp2f1(0.5*(self.dim+2*s), s-self.beta, 0.5*self.dim, r2) + elif self.derivative == 1: + s2 = s-self.eps + if self.normalized: + C = 2**(2*s) * gamma(0.5*(self.dim+2*s)) * gamma(1.+self.beta) / gamma(0.5*self.dim) / gamma(self.beta-s+1.) + dC_ds = (2.*log(2.) + digamma(0.5*(self.dim+2*s)) + digamma(self.beta-s+1.)) * C + else: + C = gamma(1.+self.beta) / gamma(0.5*self.dim) / gamma(self.beta-s+1.) / (s * pow(pi, -0.5*self.dim) / gamma(1.0-s)) + dC_ds = (digamma(self.beta-s+1.) - 1./s - digamma(1.0-s)) * C + + val = hyp2f1(0.5*(self.dim+2*s), s-self.beta, 0.5*self.dim, r2) + val2 = hyp2f1(0.5*(self.dim+2*s2), s2-self.beta, 0.5*self.dim, r2) + + dhyp_ds = (val-val2)/self.eps + df_ds = C*dhyp_ds + dC_ds*val + + self.sFun.evalGradPtr(self.dim, &x[0], &x[0], self.sFun.numParameters, &self.grad[0]) + ds_dp = 0. + for i in range(self.sFun.numParameters): + ds_dp += self.dir[i]*self.grad[i] + return df_ds * ds_dp + + elif self.derivative == 2: + s2 = s-self.eps + s3 = s+self.eps + if self.normalized: + C = 2**(2*s) * gamma(0.5*(self.dim+2*s)) * gamma(1.+self.beta) / gamma(0.5*self.dim) / gamma(self.beta-s+1.) + dC_ds = (2.*log(2.) + digamma(0.5*(self.dim+2*s)) + digamma(self.beta-s+1.)) * C + d2C_ds2 = (polygamma(1, 0.5*(self.dim+2*s)) - polygamma(1, self.beta-s+1.) + (2.*log(2.) + digamma(0.5*(self.dim+2*s)) + digamma(self.beta-s+1.))**2) * C + else: + C = gamma(1.+self.beta) / gamma(0.5*self.dim) / gamma(self.beta-s+1.) / (s * pow(pi, -0.5*self.dim) / gamma(1.0-s)) + dC_ds = (digamma(self.beta-s+1.) - 1./s - digamma(1.0-s)) * C + d2C_ds2 = (-polygamma(1, self.beta-s+1.) + 1/s**2 + polygamma(1, 1.0-s) + (digamma(self.beta-s+1.) - 1./s - digamma(1.0-s))**2) * C + + val = hyp2f1(0.5*(self.dim+2*s), s-self.beta, 0.5*self.dim, r2) + val2 = hyp2f1(0.5*(self.dim+2*s2), s2-self.beta, 0.5*self.dim, r2) + val3 = hyp2f1(0.5*(self.dim+2*s3), s3-self.beta, 0.5*self.dim, r2) + + dhyp_ds = (val-val2)/self.eps + d2hyp_ds2 = (-2*val+val2+val3)/self.eps/self.eps + + d2f_ds2 = C*d2hyp_ds2 + 2*dC_ds*dhyp_ds + d2C_ds2*val + + self.sFun.evalGradPtr(self.dim, &x[0], &x[0], self.sFun.numParameters, &self.grad[0]) + d2s_dp2 = 0. + for i in range(self.sFun.numParameters): + for j in range(self.sFun.numParameters): + d2s_dp2 += self.dir[self.sFun.numParameters*i+j]*self.grad[i]*self.grad[j] + return d2f_ds2 * d2s_dp2 diff --git a/nl/PyNucleus_nl/twoPointFunctions.pxd b/nl/PyNucleus_nl/twoPointFunctions.pxd index 2d16536e..4b42562d 100644 --- a/nl/PyNucleus_nl/twoPointFunctions.pxd +++ b/nl/PyNucleus_nl/twoPointFunctions.pxd @@ -52,3 +52,4 @@ cdef class unsymTwoPoint(twoPointFunction): cdef class inverseTwoPoint(twoPointFunction): cdef: twoPointFunction f + REAL_t scale diff --git a/nl/PyNucleus_nl/twoPointFunctions.pyx b/nl/PyNucleus_nl/twoPointFunctions.pyx index 8c2ea020..dfd61c14 100644 --- a/nl/PyNucleus_nl/twoPointFunctions.pyx +++ b/nl/PyNucleus_nl/twoPointFunctions.pyx @@ -34,9 +34,6 @@ cdef class lambdaTwoPoint(twoPointFunction): super(lambdaTwoPoint, self).__init__(symmetric, 1) self.fun = fun - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - value[0] = self.fun(x, y) - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): cdef: REAL_t[::1] xA = x @@ -68,23 +65,6 @@ cdef class matrixTwoPoint(twoPointFunction): def __repr__(self): return '{}({},sym={})'.format(self.__class__.__name__, np.array(self.mat), self.symmetric) - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - cdef: - INDEX_t dim = x.shape[0] - INDEX_t i, j - REAL_t d = 0. - for i in range(dim): - self.n[i] = x[i] - y[i] - d += self.n[i]**2 - d = sqrt(d) - for i in range(dim): - self.n[i] /= d - d = 0. - for i in range(dim): - for j in range(dim): - d += self.n[i]*self.mat[i, j]*self.n[j] - value[0] = d - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): cdef: INDEX_t i, j @@ -124,18 +104,6 @@ cdef class leftRightTwoPoint(twoPointFunction): def __repr__(self): return '{}(ll={},rr={},lr={},rl={},interface={},sym={})'.format(self.__class__.__name__, self.ll, self.rr, self.lr, self.rl, self.interface, self.symmetric) - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - if x[0] < self.interface: - if y[0] < self.interface: - value[0] = self.ll - else: - value[0] = self.lr - else: - if y[0] < self.interface: - value[0] = self.rl - else: - value[0] = self.rr - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): if x[0] < self.interface: if y[0] < self.interface: @@ -163,9 +131,6 @@ cdef class interfaceTwoPoint(twoPointFunction): def __repr__(self): return '{}(horizon1={},horizon2={},left={},interface={})'.format(self.__class__.__name__, self.horizon1, self.horizon2, self.left, self.interface) - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - self.evalPtr(x.shape[0], &x[0], &y[0], &value[0]) - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): if dim == 1: if self.left: @@ -254,14 +219,6 @@ cdef class temperedTwoPoint(twoPointFunction): def __repr__(self): return '{}(lambda={})'.format(self.__class__.__name__, self.lambdaCoeff) - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - cdef: - INDEX_t i - REAL_t r = 0. - for i in range(self.dim): - r += (x[i]-y[i])*(x[i]-y[i]) - value[0] = exp(-self.lambdaCoeff*sqrt(r)) - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): cdef: INDEX_t i @@ -287,18 +244,6 @@ cdef class tensorTwoPoint(twoPointFunction): def __repr__(self): return '{}(i={},j={})'.format(self.__class__.__name__, self.i, self.j) - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - cdef: - INDEX_t i - REAL_t n2 = 0., ExE - for i in range(self.dim): - n2 += (x[i]-y[i])*(x[i]-y[i]) - if n2 > 0: - ExE = (x[self.i]-y[self.i])*(x[self.j]-y[self.j])/n2 - else: - ExE = 1. - value[0] = ExE - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): cdef: INDEX_t i @@ -327,13 +272,6 @@ cdef class smoothedLeftRightTwoPoint(twoPointFunction): def __repr__(self): return '{}(vl={},vr={},r={},slope={})'.format(self.__class__.__name__, self.vl, self.vr, self.r, self.slope) - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - if x[0] < -self.r: - value[0] = self.vl - elif x[0] > self.r: - value[0] = self.vr - value[0] = 0.5*(self.vl+self.vr)+0.5*(self.vr-self.vl)*atan(x[0]*self.slope) * self.fac - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): if x[0] < -self.r: value[0] = self.vl @@ -354,12 +292,6 @@ cdef class unsymTwoPoint(twoPointFunction): def __repr__(self): return '{}(l={},r={})'.format(self.__class__.__name__, self.l, self.r) - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - if x[0] < y[0]: - value[0] = self.l - else: - value[0] = self.r - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): if x[0] < y[0]: value[0] = self.l @@ -368,20 +300,17 @@ cdef class unsymTwoPoint(twoPointFunction): cdef class inverseTwoPoint(twoPointFunction): - def __init__(self, twoPointFunction f): + def __init__(self, twoPointFunction f, REAL_t scale=1.): super(inverseTwoPoint, self).__init__(f.symmetric, 1) self.f = f - - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, REAL_t[::1] value): - self.f.eval(x, y, value) - value[0] = 1./value[0] + self.scale = scale cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, REAL_t* value): self.f.evalPtr(dim, x, y, value) - value[0] = 1./value[0] + value[0] = self.scale/value[0] def __repr__(self): - return '1/{}'.format(self.f) + return '{}/{}'.format(self.scale, self.f) def __reduce__(self): - return inverseTwoPoint, (self.f, ) + return inverseTwoPoint, (self.f, self.scale) diff --git a/nl/PyNucleus_nl/twoPointFunctions_decl_{SCALAR}.pxi b/nl/PyNucleus_nl/twoPointFunctions_decl_{SCALAR}.pxi index 3056819f..3eacf553 100644 --- a/nl/PyNucleus_nl/twoPointFunctions_decl_{SCALAR}.pxi +++ b/nl/PyNucleus_nl/twoPointFunctions_decl_{SCALAR}.pxi @@ -9,7 +9,6 @@ cdef class {SCALAR_label}twoPointFunction: cdef: public BOOL_t symmetric public INDEX_t valueSize - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, {SCALAR}_t[::1] value) cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, {SCALAR}_t* value) diff --git a/nl/PyNucleus_nl/twoPointFunctions_{SCALAR}.pxi b/nl/PyNucleus_nl/twoPointFunctions_{SCALAR}.pxi index 00bd8318..a7f37354 100644 --- a/nl/PyNucleus_nl/twoPointFunctions_{SCALAR}.pxi +++ b/nl/PyNucleus_nl/twoPointFunctions_{SCALAR}.pxi @@ -16,15 +16,12 @@ cdef class {SCALAR_label}twoPointFunction: def __call__(self, REAL_t[::1] x, REAL_t[::1] y): cdef: {SCALAR}_t[::1] value = uninitialized((self.valueSize), dtype={SCALAR}) - self.eval(x, y, value) + self.evalPtr(x.shape[0], &x[0], &y[0], &value[0]) if self.valueSize == 1: return value[0] else: return np.array(value, copy=False) - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, {SCALAR}_t[::1] value): - raise NotImplementedError() - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, {SCALAR}_t* value): raise NotImplementedError() @@ -115,12 +112,9 @@ cdef class {SCALAR_label}productTwoPoint({SCALAR_label}twoPointFunction): self.f1 = f1 self.f2 = f2 - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, {SCALAR}_t[::1] value): - cdef: - {SCALAR}_t val1, val2 - self.f1.evalPtr(x.shape[0], &x[0], &y[0], &val1) - self.f2.evalPtr(x.shape[0], &x[0], &y[0], &val2) - value[0] = val1*val2 + def getLongDescription(self): + return "{} * {}".format(self.f1.getLongDescription(), + self.f2.getLongDescription()) cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, {SCALAR}_t* value): cdef: @@ -144,9 +138,6 @@ cdef class {SCALAR_label}constantTwoPoint({SCALAR_label}twoPointFunction): def getLongDescription(self): return "{}".format(self.value) - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, {SCALAR}_t[::1] value): - value[0] = self.value - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, {SCALAR}_t* value): value[0] = self.value @@ -169,44 +160,6 @@ cdef class {SCALAR_label}lookupTwoPoint({SCALAR_label}twoPointFunction): self.vals1 = uninitialized((self.dm.dofs_per_element), dtype={SCALAR}) self.vals2 = uninitialized((self.dm.dofs_per_element), dtype={SCALAR}) - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, {SCALAR}_t[::1] value): - cdef: - shapeFunction shapeFun - REAL_t val - INDEX_t cellNo1, cellNo2, dof1, dof2, k1, k2 - cellNo1 = self.cellFinder.findCell(x) - if cellNo1 == -1: - value[0] = 0. - return - for k1 in range(self.dm.dofs_per_element): - dof2 = self.dm.cell2dof(cellNo1, k1) - if dof2 >= 0: - shapeFun = self.dm.getLocalShapeFunction(k1) - shapeFun.evalPtr(&self.cellFinder.bary[0], NULL, &val) - self.vals1[k1] = val - else: - self.vals1[k1] = 0. - - cellNo2 = self.cellFinder.findCell(y) - if cellNo2 == -1: - value[0] = 0. - return - for k2 in range(self.dm.dofs_per_element): - dof1 = self.dm.cell2dof(cellNo2, k2) - if dof1 >= 0: - shapeFun = self.dm.getLocalShapeFunction(k2) - shapeFun.evalPtr(&self.cellFinder.bary[0], NULL, &val) - self.vals2[k2] = val - else: - self.vals2[k2] = 0. - - value[0] = 0. - for k1 in range(self.dm.dofs_per_element): - dof1 = self.dm.cell2dof(cellNo1, k1) - for k2 in range(self.dm.dofs_per_element): - dof2 = self.dm.cell2dof(cellNo2, k2) - value[0] += self.vals1[k1]*self.A[dof1, dof2]*self.vals2[k2] - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, {SCALAR}_t* value): cdef: shapeFunction shapeFun @@ -252,6 +205,23 @@ cdef class {SCALAR_label}lookupTwoPoint({SCALAR_label}twoPointFunction): return {SCALAR_label}lookupTwoPoint, (self.dm, np.array(self.A), self.symmetric) +from libc.math cimport sin + + +cdef class {SCALAR_label}volumeElementSpherical({SCALAR_label}twoPointFunction): + def __init__(self): + super({SCALAR_label}volumeElementSpherical, self).__init__(True, 1) + + cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, {SCALAR}_t* value): + value[0] = sin(x[0]) * sin(y[0]) + + def __repr__(self): + return 'volumeElementSpherical' + + def __reduce__(self): + return {SCALAR_label}volumeElementSpherical, () + + cdef class {SCALAR_label}parametrizedTwoPointFunction({SCALAR_label}twoPointFunction): def __init__(self, BOOL_t symmetric, INDEX_t valueSize): super({SCALAR_label}parametrizedTwoPointFunction, self).__init__(symmetric, valueSize) @@ -274,6 +244,10 @@ cdef class {SCALAR_label}productParametrizedTwoPoint({SCALAR_label}parametrizedT self.f1 = f1 self.f2 = f2 + def getLongDescription(self): + return "{} * {}".format(self.f1.getLongDescription(), + self.f2.getLongDescription()) + cdef void setParams(self, void *params): cdef: parametrizedTwoPointFunction f @@ -285,13 +259,6 @@ cdef class {SCALAR_label}productParametrizedTwoPoint({SCALAR_label}parametrizedT f.setParams(params) {SCALAR_label}parametrizedTwoPointFunction.setParams(self, params) - cdef void eval(self, REAL_t[::1] x, REAL_t[::1] y, {SCALAR}_t[::1] value): - cdef: - {SCALAR}_t val1, val2 - self.f1.evalPtr(x.shape[0], &x[0], &y[0], &val1) - self.f2.evalPtr(x.shape[0], &x[0], &y[0], &val2) - value[0] = val1*val2 - cdef void evalPtr(self, INDEX_t dim, REAL_t* x, REAL_t* y, {SCALAR}_t* value): cdef: {SCALAR}_t val1, val2 diff --git a/nl/PyNucleus_nl/zeta.pxd b/nl/PyNucleus_nl/zeta.pxd new file mode 100644 index 00000000..b8aa3854 --- /dev/null +++ b/nl/PyNucleus_nl/zeta.pxd @@ -0,0 +1,8 @@ +################################################################################### +# Copyright 2021 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. # +# If you want to use this code, please refer to the README.rst and LICENSE files. # +################################################################################### + +cdef double zetaCy(double x, double q) diff --git a/nl/PyNucleus_nl/zeta.pyx b/nl/PyNucleus_nl/zeta.pyx new file mode 100644 index 00000000..df1dc3bb --- /dev/null +++ b/nl/PyNucleus_nl/zeta.pyx @@ -0,0 +1,12 @@ +################################################################################### +# Copyright 2021 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. # +# If you want to use this code, please refer to the README.rst and LICENSE files. # +################################################################################### + +cdef extern from "zeta.h" namespace "special::cephes": + float zeta(float, float) + +cdef double zetaCy(double x, double q) noexcept: + return zeta(x, q) diff --git a/nl/PyNucleus_nl/zetaXsf.pyx b/nl/PyNucleus_nl/zetaXsf.pyx new file mode 100644 index 00000000..00fbf8fa --- /dev/null +++ b/nl/PyNucleus_nl/zetaXsf.pyx @@ -0,0 +1,15 @@ +################################################################################### +# Copyright 2021 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. # +# If you want to use this code, please refer to the README.rst and LICENSE files. # +################################################################################### + +import numpy as np +cimport numpy as np + +cdef extern from "zeta.h" namespace "xsf::cephes": + double zeta(double, double) + +cdef double zetaCy(double x, double q) noexcept: + return zeta(x, q) diff --git a/nl/PyNucleus_nl/zeta_slow.pyx b/nl/PyNucleus_nl/zeta_slow.pyx new file mode 100644 index 00000000..da547eed --- /dev/null +++ b/nl/PyNucleus_nl/zeta_slow.pyx @@ -0,0 +1,11 @@ +################################################################################### +# Copyright 2021 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. # +# If you want to use this code, please refer to the README.rst and LICENSE files. # +################################################################################### + +from scipy.special import zeta + +cdef double zetaCy(double q, double s): + return zeta(q, s) diff --git a/nl/setup.py b/nl/setup.py index ce8b3282..c8f517c0 100644 --- a/nl/setup.py +++ b/nl/setup.py @@ -8,6 +8,7 @@ from shutil import move, copy from os import remove from importlib.metadata import version +from packaging.version import parse from distutils.errors import CompileError try: @@ -66,6 +67,7 @@ 'twoPointFunctions_{SCALAR}.pxi', 'twoPointFunctions_decl_{SCALAR}.pxi', 'nonlocalOperator_{SCALAR}.pxi', 'nonlocalOperator_decl_{SCALAR}.pxi', 'nonlocalAssembly_{SCALAR}.pxi', 'nonlocalAssembly_decl_{SCALAR}.pxi', + 'kernels_{SCALAR}.pxi', 'kernels_decl_{SCALAR}.pxi', ] replacementGroups = [[('{SCALAR}', 'REAL'), ('{SCALAR_label}', ''), @@ -108,13 +110,43 @@ sources=[p.folder+"interactionDomains.pyx"]) p.addExtension("kernelNormalization", sources=[p.folder+"kernelNormalization.pyx"]) -p.addExtension("kernelsCy", - sources=[p.folder+"kernelsCy.pyx"]) +p.addExtension("kernels", + sources=[p.folder+"kernels.pyx"]) p.addExtension("fractionalOrders", sources=[p.folder+"fractionalOrders.pyx"]) -p.addExtension("clusterMethodCy", - sources=[p.folder+"clusterMethodCy.pyx"], +p.addExtension("clusterMethod", + sources=[p.folder+"clusterMethod.pyx"], language='c++') +p.addExtension("nonlocal_functions", + sources=[p.folder+"nonlocal_functions.pyx"], + language='c++') +if parse(version('scipy')) >= parse('1.16'): + import scipy + import inspect + from pathlib import Path + p.addExtension("zeta", + sources=[p.folder+"zeta_slow.pyx"], + language='c++') +elif parse(version('scipy')) >= parse('1.15'): + import scipy + import inspect + from pathlib import Path + p.addExtension("zeta", + sources=[p.folder+"zetaXsf.pyx"], + language='c++', + includeDirs=[str(Path(inspect.getfile(scipy.special)).parent/'xsf'/'cephes')] + ) +elif parse(version('scipy')) >= parse('1.13'): + import scipy + import inspect + from pathlib import Path + p.addExtension("zeta", + sources=[p.folder+"zeta.pyx"], + language='c++', + includeDirs=[str(Path(inspect.getfile(scipy.special)).parent/'special'/'cephes')] + ) +else: + raise NotImplementedError() p.setup(description="Nonlocal operator assembly", python_requires='>=3.10', diff --git a/pytest.ini b/pytest.ini index c29cd39a..49dfd0ef 100644 --- a/pytest.ini +++ b/pytest.ini @@ -3,6 +3,7 @@ filterwarnings = ignore:unit_vertices:DeprecationWarning markers = slow: marks tests as slow (deselect with '-m "not slow"') + fast: all tests that are not slow testpaths = tests generate_report_on_test = True \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 15f340f8..0783ba34 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,6 +10,7 @@ scipy>=1.13 matplotlib MeshPy +gmsh gmsh_interop h5py meshio diff --git a/tests/cache_runFractional.py--domainball--sconst(0.25)--problemconstant--elementP1--solvercg-mg--matrixFormatH24 b/tests/cache_runFractional.py--domainball--sconst(0.25)--problemconstant--elementP1--solvercg-mg--matrixFormatH24 new file mode 100644 index 00000000..41007e71 --- /dev/null +++ b/tests/cache_runFractional.py--domainball--sconst(0.25)--problemconstant--elementP1--solvercg-mg--matrixFormatH24 @@ -0,0 +1,14 @@ +Timers: {} +errors: + Hs error: 0.19436753037164584 + L2 error: 0.31685843116736806 + L2 error interpolated: 0.09110776467079598 + Linf error interpolated: 0.1906688981681709 + relative Hs error: 0.14135848649433813 + relative L2 error: 0.29105305389706404 + relative interpolated L2 error: 0.08946944576349168 + relative interpolated Linf error: 0.2534638671045646 +meshes: {} +results: {} +sysInfo: {} +vectors: {} diff --git a/tests/cache_runFractional.py--domainball--sconst(0.75)--problemconstant--elementP1--solvercg-mg--matrixFormatH24 b/tests/cache_runFractional.py--domainball--sconst(0.75)--problemconstant--elementP1--solvercg-mg--matrixFormatH24 new file mode 100644 index 00000000..0fb7f39d --- /dev/null +++ b/tests/cache_runFractional.py--domainball--sconst(0.75)--problemconstant--elementP1--solvercg-mg--matrixFormatH24 @@ -0,0 +1,14 @@ +Timers: {} +errors: + Hs error: 0.38204125536740047 + L2 error: 0.11859018779024592 + L2 error interpolated: 0.09087577694395707 + Linf error interpolated: 0.08728311355943749 + relative Hs error: 0.5198073973931333 + relative L2 error: 0.3515766577165007 + relative interpolated L2 error: 0.2859129555656763 + relative interpolated Linf error: 0.2900724201514657 +meshes: {} +results: {} +sysInfo: {} +vectors: {} diff --git a/tests/cache_runFractionalHeat.py--domainball--sconst(0.25)--problemconstant--elementP1--solvercg-mg--matrixFormatH24 b/tests/cache_runFractionalHeat.py--domainball--sconst(0.25)--problemconstant--elementP1--solvercg-mg--matrixFormatH24 new file mode 100644 index 00000000..6b31b230 --- /dev/null +++ b/tests/cache_runFractionalHeat.py--domainball--sconst(0.25)--problemconstant--elementP1--solvercg-mg--matrixFormatH24 @@ -0,0 +1,9 @@ +Timers: {} +errors: + L^2(0,T; L^2(Omega)) error: 0.355501545228383 + L^2(0,T; L^2(Omega)) norm: 1.2261111503475945 + L^2(Omega) error at t=finalTime: 0.163858454621266 +meshes: {} +results: {} +sysInfo: {} +vectors: {} diff --git a/tests/cache_runFractionalHeat.py--domainball--sconst(0.75)--problemconstant--elementP1--solvercg-mg--matrixFormatH24 b/tests/cache_runFractionalHeat.py--domainball--sconst(0.75)--problemconstant--elementP1--solvercg-mg--matrixFormatH24 new file mode 100644 index 00000000..67476400 --- /dev/null +++ b/tests/cache_runFractionalHeat.py--domainball--sconst(0.75)--problemconstant--elementP1--solvercg-mg--matrixFormatH24 @@ -0,0 +1,9 @@ +Timers: {} +errors: + L^2(0,T; L^2(Omega)) error: 0.1464232929028331 + L^2(0,T; L^2(Omega)) norm: 0.2931989174975604 + L^2(Omega) error at t=finalTime: 0.06571952080940717 +meshes: {} +results: {} +sysInfo: {} +vectors: {} diff --git a/tests/cache_runNonlocal.py--domaininterval--kernelTypeinverseDistance--problempoly-Dirichlet--solverlu--matrixFormatH2 b/tests/cache_runNonlocal.py--domaininterval--kernelTypeinverseDistance--problempoly-Dirichlet--solverlu--matrixFormatH2 index b6e41c6b..dc7b563c 100644 --- a/tests/cache_runNonlocal.py--domaininterval--kernelTypeinverseDistance--problempoly-Dirichlet--solverlu--matrixFormatH2 +++ b/tests/cache_runNonlocal.py--domaininterval--kernelTypeinverseDistance--problempoly-Dirichlet--solverlu--matrixFormatH2 @@ -1,9 +1,10 @@ Timers: {} errors: - L2 error interpolated: 3.6453345420885625e-08 - Linf error interpolated: 3.885486987709186e-08 - relative interpolated L2 error: 3.4894725990076044e-08 - relative interpolated Linf error: 3.885486987709186e-08 + L2 error interpolated: 1.691018341114367e-07 + Linf error interpolated: 1.8024266146277057e-07 + relative interpolated L2 error: 1.6187162241512917e-07 + relative interpolated Linf error: 1.8024266146277057e-07 meshes: {} results: {} +sysInfo: {} vectors: {} diff --git a/tests/cache_runNonlocal.py--domainsquare--kernelTypeconstant--problemquadratic(1.0,0.5,0.3)--solvercg-mg--matrixFormatH2--interactionellipse(0.5,1.0,0.) b/tests/cache_runNonlocal.py--domainsquare--kernelTypeconstant--problemquadratic(1.0,0.5,0.3)--solvercg-mg--matrixFormatH2--interactionellipse(0.5,1.0,0.) deleted file mode 100644 index 158d31e4..00000000 --- a/tests/cache_runNonlocal.py--domainsquare--kernelTypeconstant--problemquadratic(1.0,0.5,0.3)--solvercg-mg--matrixFormatH2--interactionellipse(0.5,1.0,0.) +++ /dev/null @@ -1,10 +0,0 @@ -Timers: {} -errors: - L2 error interpolated: 0.0016344186228406023 - Linf error interpolated: 0.0021662405806308627 - relative interpolated L2 error: 0.0009119929375692566 - relative interpolated Linf error: 0.0010687836012114231 -meshes: {} -results: {} -sysInfo: {} -vectors: {} diff --git a/tests/cache_runNonlocal.py--domainsquare--kernelTypeconstant--problemquadratic2d(1.0,0.5,0.3)--solvercg-mg--matrixFormatH2--interactionellipse(0.5,1.0,0.) b/tests/cache_runNonlocal.py--domainsquare--kernelTypeconstant--problemquadratic2d(1.0,0.5,0.3)--solvercg-mg--matrixFormatH2--interactionellipse(0.5,1.0,0.) new file mode 100644 index 00000000..51c7bf4d --- /dev/null +++ b/tests/cache_runNonlocal.py--domainsquare--kernelTypeconstant--problemquadratic2d(1.0,0.5,0.3)--solvercg-mg--matrixFormatH2--interactionellipse(0.5,1.0,0.) @@ -0,0 +1,10 @@ +Timers: {} +errors: + L2 error interpolated: 0.001423847314520384 + Linf error interpolated: 0.0031589249434635924 + relative interpolated L2 error: 0.0006664841671398514 + relative interpolated Linf error: 0.001043642021590368 +meshes: {} +results: {} +sysInfo: {} +vectors: {} diff --git a/tests/cache_runNonlocal.py--domainsquare--kernelTypefractional--problemquadratic(1.0,0.5,0.3)--solvercg-mg--matrixFormatH2--interactionellipse(0.5,1.0,0.) b/tests/cache_runNonlocal.py--domainsquare--kernelTypefractional--problemquadratic(1.0,0.5,0.3)--solvercg-mg--matrixFormatH2--interactionellipse(0.5,1.0,0.) deleted file mode 100644 index 4d2a2bca..00000000 --- a/tests/cache_runNonlocal.py--domainsquare--kernelTypefractional--problemquadratic(1.0,0.5,0.3)--solvercg-mg--matrixFormatH2--interactionellipse(0.5,1.0,0.) +++ /dev/null @@ -1,10 +0,0 @@ -Timers: {} -errors: - L2 error interpolated: 0.007707502693048059 - Linf error interpolated: 0.006993612414013417 - relative interpolated L2 error: 0.004300726829788075 - relative interpolated Linf error: 0.00345052083695587 -meshes: {} -results: {} -sysInfo: {} -vectors: {} diff --git a/tests/cache_runNonlocal.py--domainsquare--kernelTypefractional--problemquadratic2d(1.0,0.5,0.3)--solvercg-mg--matrixFormatH2--interactionellipse(0.5,1.0,0.) b/tests/cache_runNonlocal.py--domainsquare--kernelTypefractional--problemquadratic2d(1.0,0.5,0.3)--solvercg-mg--matrixFormatH2--interactionellipse(0.5,1.0,0.) new file mode 100644 index 00000000..cc89369b --- /dev/null +++ b/tests/cache_runNonlocal.py--domainsquare--kernelTypefractional--problemquadratic2d(1.0,0.5,0.3)--solvercg-mg--matrixFormatH2--interactionellipse(0.5,1.0,0.) @@ -0,0 +1,10 @@ +Timers: {} +errors: + L2 error interpolated: 0.6162707878976148 + Linf error interpolated: 0.5235363799176427 + relative interpolated L2 error: 0.2884682357552613 + relative interpolated Linf error: 0.17296535235632096 +meshes: {} +results: {} +sysInfo: {} +vectors: {} diff --git a/tests/cache_runNonlocal.py--domainsquare--kernelTypeinverseDistance--problemquadratic(1.0,0.5,0.3)--solvercg-mg--matrixFormatH2--interactionellipse(0.5,1.0,0.) b/tests/cache_runNonlocal.py--domainsquare--kernelTypeinverseDistance--problemquadratic(1.0,0.5,0.3)--solvercg-mg--matrixFormatH2--interactionellipse(0.5,1.0,0.) deleted file mode 100644 index fb6df5ab..00000000 --- a/tests/cache_runNonlocal.py--domainsquare--kernelTypeinverseDistance--problemquadratic(1.0,0.5,0.3)--solvercg-mg--matrixFormatH2--interactionellipse(0.5,1.0,0.) +++ /dev/null @@ -1,10 +0,0 @@ -Timers: {} -errors: - L2 error interpolated: 0.004496665250037543 - Linf error interpolated: 0.004016610983843427 - relative interpolated L2 error: 0.0025091043954944465 - relative interpolated Linf error: 0.0019817226167590956 -meshes: {} -results: {} -sysInfo: {} -vectors: {} diff --git a/tests/cache_runNonlocal.py--domainsquare--kernelTypeinverseDistance--problemquadratic2d(1.0,0.5,0.3)--solvercg-mg--matrixFormatH2--interactionellipse(0.5,1.0,0.) b/tests/cache_runNonlocal.py--domainsquare--kernelTypeinverseDistance--problemquadratic2d(1.0,0.5,0.3)--solvercg-mg--matrixFormatH2--interactionellipse(0.5,1.0,0.) new file mode 100644 index 00000000..4aeabef0 --- /dev/null +++ b/tests/cache_runNonlocal.py--domainsquare--kernelTypeinverseDistance--problemquadratic2d(1.0,0.5,0.3)--solvercg-mg--matrixFormatH2--interactionellipse(0.5,1.0,0.) @@ -0,0 +1,10 @@ +Timers: {} +errors: + L2 error interpolated: 0.543961780966439 + Linf error interpolated: 0.4583369925764657 + relative interpolated L2 error: 0.2546213423631366 + relative interpolated Linf error: 0.15142485309501472 +meshes: {} +results: {} +sysInfo: {} +vectors: {} diff --git a/tests/cache_runNonlocalInterface.py--domaindoubleInterval--no-forceRebuild--hTarget0.049 b/tests/cache_runNonlocalInterface.py--domaindoubleInterval--no-forceRebuild--hTarget0.049 new file mode 100644 index 00000000..34435188 --- /dev/null +++ b/tests/cache_runNonlocalInterface.py--domaindoubleInterval--no-forceRebuild--hTarget0.049 @@ -0,0 +1,14 @@ +Timers: {} +data: {} +errors: + L2 error domain 1: 0.0017881503707411876 + L2 error domain 2: 0.0008025169519814404 +results: + DoFs domain1DoFMap: 22 + DoFs domain2DoFMap: 23 + DoFs fullDomain1DoFMap: 30 + DoFs fullDomain2DoFMap: 35 + h fullDomain1Mesh: 0.04705882352941182 + h fullDomain2Mesh: 0.04705882352941182 + hmin fullDomain1Mesh: 0.033333333333333215 + hmin fullDomain2Mesh: 0.033333333333333215 diff --git a/tests/cache_runNonlocalInterface.py--domaindoubleSquare--no-forceRebuild--hTarget0.049 b/tests/cache_runNonlocalInterface.py--domaindoubleSquare--no-forceRebuild--hTarget0.049 new file mode 100644 index 00000000..318258c9 --- /dev/null +++ b/tests/cache_runNonlocalInterface.py--domaindoubleSquare--no-forceRebuild--hTarget0.049 @@ -0,0 +1,14 @@ +Timers: {} +data: {} +errors: + L2 error domain 1: 2.7592060939648685e-05 + L2 error domain 2: 2.706758060007906e-05 +results: + DoFs domain1DoFMap: 1554 + DoFs domain2DoFMap: 1579 + DoFs fullDomain1DoFMap: 2391 + DoFs fullDomain2DoFMap: 3211 + h fullDomain1Mesh: 0.04516307680897048 + h fullDomain2Mesh: 0.044785790441323345 + hmin fullDomain1Mesh: 0.016411330864556567 + hmin fullDomain2Mesh: 0.015625 diff --git a/tests/test_drivers_intFracLapl.py b/tests/test_drivers_intFracLapl.py index 33a47c60..e947afa2 100644 --- a/tests/test_drivers_intFracLapl.py +++ b/tests/test_drivers_intFracLapl.py @@ -6,13 +6,12 @@ ################################################################################### from PyNucleus_base.utilsFem import runDriver -import os -import inspect import pytest -def getPath(): - return os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) +@pytest.fixture() +def path(request): + return request.path.parent def idfunc(param): @@ -54,13 +53,12 @@ def runNonlocal_params(request): @pytest.mark.slow -def testNonlocal(runNonlocal_params, extras): +def testNonlocal(runNonlocal_params, path, extras): if len(runNonlocal_params) == 5: domain, kernel, problem, solver, matrixFormat = runNonlocal_params interaction = None else: domain, kernel, problem, solver, matrixFormat, interaction = runNonlocal_params - base = getPath()+'/../' py = ['runNonlocal.py', '--domain', domain, '--kernelType', kernel, @@ -76,11 +74,9 @@ def testNonlocal(runNonlocal_params, extras): if interaction == 'fullSpace': py += ['--horizon', 'inf'] # if kernel != 'fractional': - path = base+'drivers' - cacheDir = getPath()+'/' if problem == 'poly-Neumann' and domain == 'square': return pytest.skip('not implemented') - runDriver(path, py, cacheDir=cacheDir, extra=extras) + runDriver(path/'../drivers', py, cacheDir=path, extra=extras) @pytest.fixture(scope='module', params=[ @@ -120,9 +116,8 @@ def runFractional_params(request): @pytest.mark.slow -def testFractional(runFractional_params, extras): +def testFractional(runFractional_params, path, extras): domain, s, problem, element, solver, matrixFormat, ranks = runFractional_params - base = getPath()+'/../' py = ['runFractional.py', '--domain', domain, '--s', s, @@ -130,17 +125,14 @@ def testFractional(runFractional_params, extras): '--element', element, '--solver', solver, '--matrixFormat', matrixFormat] - path = base+'drivers' - cacheDir = getPath()+'/' if ranks == 1: ranks = None - runDriver(path, py, cacheDir=cacheDir, extra=extras, ranks=ranks) + runDriver(path/'../drivers', py, cacheDir=path, extra=extras, ranks=ranks) @pytest.mark.slow -def testFractionalHeat(runFractional_params, extras): +def testFractionalHeat(runFractional_params, path, extras): domain, s, problem, element, solver, matrixFormat, ranks = runFractional_params - base = getPath()+'/../' py = ['runFractionalHeat.py', '--domain', domain, '--s', s, @@ -148,47 +140,37 @@ def testFractionalHeat(runFractional_params, extras): '--element', element, '--solver', solver, '--matrixFormat', matrixFormat] - path = base+'drivers' - cacheDir = getPath()+'/' if ranks == 1: ranks = None - runDriver(path, py, cacheDir=cacheDir, extra=extras, ranks=ranks) + runDriver(path/'../drivers', py, cacheDir=path, extra=extras, ranks=ranks) @pytest.mark.slow -def testVariableOrder(extras): - base = getPath()+'/../' +def testVariableOrder(path, extras): py = 'variableOrder.py' - path = base+'drivers' - cacheDir = getPath()+'/' - runDriver(path, py, cacheDir=cacheDir, extra=extras) - - -@pytest.fixture(scope='module', params=[ - ('interval', 'const(0.25)'), - ('interval', 'const(0.75)'), - ('interval', 'varconst(0.25)'), - ('interval', 'varconst(0.75)'), - ('interval', 'twoDomainNonSym(0.25,0.75)'), - ('disc', 'const(0.25)'), - ('disc', 'const(0.75)'), - ('disc', 'varconst(0.25)'), - ('disc', 'varconst(0.75)'), - ('square', 'const(0.25)'), - ('square', 'const(0.75)'), - ('square', 'varconst(0.25)'), - ('square', 'varconst(0.75)'), - ('square', 'twoDomainNonSym(0.25,0.75)'), -], - ids=idfunc) -def runDistOp_params(request): - return request.param + runDriver(path/'../drivers', py, cacheDir=path, extra=extras) @pytest.mark.slow -def testMatvecs(runDistOp_params, extras): - base = getPath()+'/../' - domain, fractionalOrder = runDistOp_params +@pytest.mark.parametrize( + "domain, fractionalOrder", + [ + ('interval', 'const(0.25)'), + ('interval', 'const(0.75)'), + ('interval', 'varconst(0.25)'), + ('interval', 'varconst(0.75)'), + ('interval', 'twoDomainNonSym(0.25,0.75)'), + ('disc', 'const(0.25)'), + ('disc', 'const(0.75)'), + ('disc', 'varconst(0.25)'), + ('disc', 'varconst(0.75)'), + ('square', 'const(0.25)'), + ('square', 'const(0.75)'), + ('square', 'varconst(0.25)'), + ('square', 'varconst(0.75)'), + ('square', 'twoDomainNonSym(0.25,0.75)'), + ]) +def testMatvecs(domain, fractionalOrder, path, extras): if domain == 'interval': noRef = 6 elif domain == 'disc': @@ -208,36 +190,29 @@ def testMatvecs(runDistOp_params, extras): '--buildDistributedH2', '--doSolve'] py += ['--no-write'] - path = base+'drivers' - cacheDir = getPath()+'/' - runDriver(path, py, ranks=4, cacheDir=cacheDir, extra=extras) - - -@pytest.fixture(scope='module', params=[ - ('doubleInterval', 'fractional', 'fractional', '0.2', '0.4', '0.2', '0.2', 'exact-sin-variableSolJump-fluxJump'), - ('doubleInterval', 'fractional', 'fractional', '0.2', '0.4', '0.2', '0.4', 'exact-sin-variableSolJump-fluxJump'), - ('doubleInterval', 'indicator', 'indicator', '0.2', '0.4', '0.2', '0.2', 'exact-sin-variableSolJump-fluxJump'), - ('doubleInterval', 'indicator', 'indicator', '0.2', '0.4', '0.2', '0.4', 'exact-sin-variableSolJump-fluxJump'), - ('doubleInterval', 'indicator', 'fractional', '0.2', '0.4', '0.2', '0.2', 'exact-sin-variableSolJump-fluxJump'), - ('doubleInterval', 'indicator', 'fractional', '0.2', '0.4', '0.2', '0.4', 'exact-sin-variableSolJump-fluxJump'), - ('doubleSquare', 'fractional', 'fractional', '0.2', '0.4', '0.2', '0.2', 'sin-variableSolJump-fluxJump'), - ('doubleSquare', 'fractional', 'fractional', '0.2', '0.4', '0.2', '0.4', 'sin-variableSolJump-fluxJump'), - ('doubleSquare', 'indicator', 'indicator', '0.2', '0.4', '0.2', '0.2', 'sin-variableSolJump-fluxJump'), - ('doubleSquare', 'indicator', 'indicator', '0.2', '0.4', '0.2', '0.4', 'sin-variableSolJump-fluxJump'), - ('doubleSquare', 'indicator', 'fractional', '0.2', '0.4', '0.2', '0.2', 'sin-variableSolJump-fluxJump'), - ('doubleSquare', 'indicator', 'fractional', '0.2', '0.4', '0.2', '0.4', 'sin-variableSolJump-fluxJump'), -], - ids=idfunc) -def runNonlocalInterface_params(request): - return request.param + runDriver(path/'../drivers', py, ranks=4, cacheDir=path, extra=extras) @pytest.mark.slow -def testNonlocalInterface(runNonlocalInterface_params, extras): - domain, kernel1, kernel2, s11, s22, horizon1, horizon2, problem = runNonlocalInterface_params +@pytest.mark.parametrize( + "domain, kernel1, kernel2, s11, s22, horizon1, horizon2, problem", + [ + ('doubleInterval', 'fractional', 'fractional', '0.2', '0.4', '0.2', '0.2', 'exact-sin-variableSolJump-fluxJump'), + ('doubleInterval', 'fractional', 'fractional', '0.2', '0.4', '0.2', '0.4', 'exact-sin-variableSolJump-fluxJump'), + ('doubleInterval', 'indicator', 'indicator', '0.2', '0.4', '0.2', '0.2', 'exact-sin-variableSolJump-fluxJump'), + ('doubleInterval', 'indicator', 'indicator', '0.2', '0.4', '0.2', '0.4', 'exact-sin-variableSolJump-fluxJump'), + ('doubleInterval', 'indicator', 'fractional', '0.2', '0.4', '0.2', '0.2', 'exact-sin-variableSolJump-fluxJump'), + ('doubleInterval', 'indicator', 'fractional', '0.2', '0.4', '0.2', '0.4', 'exact-sin-variableSolJump-fluxJump'), + ('doubleSquare', 'fractional', 'fractional', '0.2', '0.4', '0.2', '0.2', 'sin-variableSolJump-fluxJump'), + ('doubleSquare', 'fractional', 'fractional', '0.2', '0.4', '0.2', '0.4', 'sin-variableSolJump-fluxJump'), + ('doubleSquare', 'indicator', 'indicator', '0.2', '0.4', '0.2', '0.2', 'sin-variableSolJump-fluxJump'), + ('doubleSquare', 'indicator', 'indicator', '0.2', '0.4', '0.2', '0.4', 'sin-variableSolJump-fluxJump'), + ('doubleSquare', 'indicator', 'fractional', '0.2', '0.4', '0.2', '0.2', 'sin-variableSolJump-fluxJump'), + ('doubleSquare', 'indicator', 'fractional', '0.2', '0.4', '0.2', '0.4', 'sin-variableSolJump-fluxJump'), + ]) +def testNonlocalInterface(domain, kernel1, kernel2, s11, s22, horizon1, horizon2, problem, path, extras): s12 = s11 s21 = s22 - base = getPath()+'/../' py = ['runNonlocalInterface.py', '--domain', domain, '--kernel1', kernel1, @@ -249,6 +224,4 @@ def testNonlocalInterface(runNonlocalInterface_params, extras): '--horizon1', horizon1, '--horizon2', horizon2, '--problem', problem] - path = base+'drivers' - cacheDir = getPath()+'/' - runDriver(path, py, cacheDir=cacheDir, extra=extras) + runDriver(path/'../drivers', py, cacheDir=path, extra=extras) diff --git a/tests/test_fracLapl.py b/tests/test_fracLapl.py index 0c9b638e..b604b7a1 100644 --- a/tests/test_fracLapl.py +++ b/tests/test_fracLapl.py @@ -13,12 +13,13 @@ from PyNucleus_fem.functions import constant from PyNucleus_nl.nonlocalAssembly import (assembleNonlocalOperator, nonlocalBuilder) -from PyNucleus_nl.clusterMethodCy import H2Matrix +from PyNucleus_nl.clusterMethod import H2Matrix from PyNucleus_base.myTypes import REAL from scipy.special import gamma -from PyNucleus_nl.kernels import getFractionalKernel +from PyNucleus_nl.kernels import FractionalKernel from PyNucleus_nl.fractionalOrders import constFractionalOrder from PyNucleus_nl.kernelNormalization import variableFractionalLaplacianScaling +from PyNucleus_nl.factories import fractionalOrderFactory import pytest import logging LOGGER = logging.getLogger(__name__) @@ -89,9 +90,9 @@ def scaling(dim, s, horizon, refinements): mesh = mesh.refine() dm = P1_DoFMap(mesh, tag=0) - kernel1 = getFractionalKernel(mesh.dim, s, horizon) - scaling = variableFractionalLaplacianScaling(True) - kernel2 = getFractionalKernel(mesh.dim, s, horizon, scaling=scaling) + kernel1 = FractionalKernel.build(dim=mesh.dim, s=s, horizon=horizon) + variable_s = fractionalOrderFactory('varconst', s=s.value) + kernel2 = FractionalKernel.build(dim=mesh.dim, s=variable_s, horizon=horizon) print(kernel1, kernel2) zeroExterior = not np.isfinite(horizon.value) builder1 = nonlocalBuilder(dm, kernel1, zeroExterior=zeroExterior) @@ -159,7 +160,7 @@ def h2(dim, s, refinements, element, errBnd, genKernel=False): params['genKernel'] = genKernel params['eta'] = eta params['maxLevels'] = maxLevels - kernel = getFractionalKernel(mesh.dim, s, constant(np.inf)) + kernel = FractionalKernel.build(dim=mesh.dim, s=s, horizon=constant(np.inf)) builder = nonlocalBuilder(DoFMap_fine, kernel, params=params, zeroExterior=True) A_d = np.array(builder.getDense().data) diff --git a/tests/test_h2finiteHorizon.py b/tests/test_h2finiteHorizon.py index fd076052..000d092b 100644 --- a/tests/test_h2finiteHorizon.py +++ b/tests/test_h2finiteHorizon.py @@ -13,7 +13,7 @@ variableConstFractionalOrder) from PyNucleus_nl.nonlocalAssembly import nonlocalBuilder from PyNucleus_nl.kernelNormalization import variableFractionalLaplacianScaling -from PyNucleus_nl.kernels import getFractionalKernel +from PyNucleus_nl.kernels import FractionalKernel from scipy.linalg import solve import pytest @@ -34,8 +34,8 @@ def idfunc(param): ids=idfunc) def kernels(request): dim, s, horizon1, horizon2, normalized = request.param - kernel1 = getFractionalKernel(dim, s, constant(horizon1), normalized=normalized) - kernel2 = getFractionalKernel(dim, s, constant(horizon2), normalized=normalized) + kernel1 = FractionalKernel.build(dim=dim, s=s, horizon=constant(horizon1), normalized=normalized) + kernel2 = FractionalKernel.build(dim=dim, s=s, horizon=constant(horizon2), normalized=normalized) return dim, kernel1, kernel2 diff --git a/tests/test_kernels.py b/tests/test_kernels.py index 4cac1f3f..6a08ba16 100644 --- a/tests/test_kernels.py +++ b/tests/test_kernels.py @@ -6,13 +6,20 @@ ################################################################################### import numpy as np +from PyNucleus import REAL from PyNucleus_nl import fractionalOrderFactory, kernelFactory, twoPointFunctionFactory +from PyNucleus_fem import meshFactory, dofmapFactory from PyNucleus_nl.twoPointFunctions import constantTwoPoint from PyNucleus_nl.fractionalOrders import (constFractionalOrder, variableConstFractionalOrder, constantNonSymFractionalOrder, smoothedLeftRightFractionalOrder, feFractionalOrder) +from PyNucleus_base.linear_operators import (Dense_LinearOperator, + Dense_VectorLinearOperator, + TimeStepperLinearOperator, + SumVectorLinearOperator) +from PyNucleus_nl.clusterMethod import H2Matrix, VectorH2Matrix from PyNucleus_fem.functions import constant from scipy.special import gamma, erf, digamma, gammaincc, polygamma from numpy import log @@ -21,33 +28,25 @@ import pytest -def idfuncIntegrable(param): - dim, kernelType, horizon, normalized = param - return f'dim{dim}-kernelType{kernelType}horizon{horizon}-normalized{normalized}' - - -@pytest.fixture(scope='module', params=[ - # 1d kernels - (1, 'constant', 0.5, True), - (1, 'constant', 0.5, False), - (1, 'inverseDistance', 0.5, True), - (1, 'inverseDistance', 0.5, False), - (1, 'Gaussian', 0.5, True), - (1, 'Gaussian', 0.5, False), - # 2d kernels - (2, 'constant', 0.5, True), - (2, 'constant', 0.5, False), - (2, 'inverseDistance', 0.5, True), - (2, 'inverseDistance', 0.5, False), - (2, 'Gaussian', 0.5, True), - (2, 'Gaussian', 0.5, False), -], ids=idfuncIntegrable) -def integrableKernelParams(request): - return request.param - - -def testIntegrableKernel(integrableKernelParams): - dim, kernelType, horizon, normalized = integrableKernelParams +@pytest.mark.parametrize( + "dim, kernelType, horizon, normalized", + [ + # 1d kernels + (1, 'constant', 0.5, True), + (1, 'constant', 0.5, False), + (1, 'inverseDistance', 0.5, True), + (1, 'inverseDistance', 0.5, False), + (1, 'Gaussian', 0.5, True), + (1, 'Gaussian', 0.5, False), + # 2d kernels + (2, 'constant', 0.5, True), + (2, 'constant', 0.5, False), + (2, 'inverseDistance', 0.5, True), + (2, 'inverseDistance', 0.5, False), + (2, 'Gaussian', 0.5, True), + (2, 'Gaussian', 0.5, False), + ]) +def testIntegrableKernel(dim, kernelType, horizon, normalized): if dim == 1: xy_values = [(np.array([0.1]), np.array([0.2])), (np.array([0.1]), np.array([0.7]))] @@ -57,6 +56,10 @@ def testIntegrableKernel(integrableKernelParams): else: raise NotImplementedError() kernel = kernelFactory(kernelType, dim=dim, horizon=horizon, normalized=normalized) + if dim == 1 and kernelType == 'inverseDistance': + assert not kernel.is_integrable + else: + assert kernel.is_integrable infHorizonKernel = kernel.getModifiedKernel(horizon=constant(np.inf)) boundaryKernelInf = infHorizonKernel.getBoundaryKernel() @@ -65,29 +68,29 @@ def testIntegrableKernel(integrableKernelParams): if normalized: if kernelType == 'constant': if dim == 1: - const = 3/horizonValue**3 * 0.5 + const = 3/horizonValue**3 elif dim == 2: - const = 8./pi/horizonValue**4 * 0.5 + const = 8./pi/horizonValue**4 else: raise NotImplementedError() elif kernelType == 'inverseDistance': if dim == 1: - const = 2./horizonValue**2 * 0.5 + const = 2./horizonValue**2 elif dim == 2: - const = 6./pi/horizonValue**3 * 0.5 + const = 6./pi/horizonValue**3 else: raise NotImplementedError() elif kernelType == 'Gaussian': if dim == 1: - const = 4.0/sqrt(pi)/(erf(3.0)-6.0*exp(-9.0)/sqrt(pi))/(horizonValue/3.0)**3 / 2. + const = 4.0/sqrt(pi)/(erf(3.0)-6.0*exp(-9.0)/sqrt(pi))/(horizonValue/3.0)**3 elif dim == 2: - const = 4.0/pi/(1.0-10.0*exp(-9.0))/(horizonValue/3.0)**4 / 2. + const = 4.0/pi/(1.0-10.0*exp(-9.0))/(horizonValue/3.0)**4 else: raise NotImplementedError() else: raise NotImplementedError() else: - const = 0.5 + const = 1.0 for x, y in xy_values: @@ -107,14 +110,14 @@ def testIntegrableKernel(integrableKernelParams): ref = 0. if kernelType == 'constant': - refBoundary = refInf*(-1/dim) + refBoundary = refInf*(1/dim) elif kernelType == 'inverseDistance': if dim == 1: - refBoundary = refInf*(-log(norm(x-y))) + refBoundary = refInf*(log(norm(x-y))) else: - refBoundary = refInf*(-1/(dim-1)) + refBoundary = refInf*(1.0/(dim-1)) elif kernelType == 'Gaussian': - refBoundary = const*0.5*(invD*norm(x-y)**2)**(-dim/2) * gamma(dim/2) * gammaincc(dim/2, invD*norm(x-y)**2) + refBoundary = -const*0.5*(invD*norm(x-y)**2)**(-dim/2) * gamma(dim/2) * gammaincc(dim/2, invD*norm(x-y)**2) else: raise NotImplementedError() @@ -122,7 +125,10 @@ def testIntegrableKernel(integrableKernelParams): assert np.isclose(kernel(x, y), ref), (kernel(x, y), ref) # test boundary kernel - assert np.isclose(boundaryKernelInf(x, y), 2*refBoundary*norm(x-y)), (boundaryKernelInf(x, y), 2*refBoundary*norm(x-y)) + n = np.zeros((dim), dtype=REAL) + n[0] = 1. + boundaryKernelInf.setNormal(n) + np.testing.assert_allclose(boundaryKernelInf(x, y), refBoundary*np.dot(x-y, n)) # test that div_y (boundaryKernelInf(x,y) (x-y)/norm(x-y)) == 2*infHorizonKernel(x,y) eps = 1e-8 @@ -130,413 +136,475 @@ def testIntegrableKernel(integrableKernelParams): for i in range(dim): yShifted = y.copy() yShifted[i] += eps - div_fd += (boundaryKernelInf(x, yShifted) * (x-yShifted)[i]/norm(x-yShifted) - boundaryKernelInf(x, y) * (x-y)[i]/norm(x-y))/eps - assert np.isclose(div_fd, 2*infHorizonKernel(x, y)), (div_fd, 2*infHorizonKernel(x, y)) + if dim == 1: + div_fd += (boundaryKernelInf(x, yShifted) - boundaryKernelInf(x, y))/eps + else: + n[:] = 0. + n[i] = 1. + boundaryKernelInf.setNormal(n) + div_fd += (boundaryKernelInf(x, yShifted) - boundaryKernelInf(x, y))/eps + np.testing.assert_allclose(div_fd, -infHorizonKernel(x, y), rtol=3e-7) + + +@pytest.mark.parametrize( + "dim, power", + [ + # 1d kernels + (1, -1.5), + (1, -2.5), + (1, -1.), + (1, 0.), + # 2d kernels + (2, -2.5), + (2, -3.5), + (2, -1.), + (2, 0.), + # 3d kernels + (3, -3.5), + (3, -4.5), + (3, -1.), + (3, 0.), + ]) +def test_monomial(dim, power): + if dim == 1: + xy_values = [(np.array([0.1]), np.array([0.2])), + (np.array([0.1]), np.array([0.7]))] + elif dim == 2: + xy_values = [(np.array([0.1, 0.1]), np.array([0.2, 0.2])), + (np.array([0.1, 0.1]), np.array([0.7, 0.2]))] + elif dim == 3: + xy_values = [(np.array([-0.1, 0.1, 0.1]), np.array([0.1, 0.2, 0.2])), + (np.array([0.1, 0.1, 0.1]), np.array([-0.1, 0.2, 0.2])), + (np.array([-0.1, 0.1, 0.1]), np.array([0.5, 0.2, 0.2]))] + else: + raise NotImplementedError() + + kernel = kernelFactory('monomial', dim=dim, horizon=0.5, normalized=False, monomialPower=power) + if -dim-2. < power < -dim: + kernel2 = kernelFactory('fractional', dim=dim, horizon=0.5, normalized=False, s=-0.5*(dim+power)) + elif power == -1.: + kernel2 = kernelFactory('inverseDistance', dim=dim, horizon=0.5, normalized=False) + elif power == 0.: + kernel2 = kernelFactory('constant', dim=dim, horizon=0.5, normalized=False) + else: + raise NotImplementedError(power) + assert kernel.is_integrable == kernel2.is_integrable + assert kernel.singularityValue == kernel2.singularityValue -def idfuncFractional(param): - dim, s, horizon, normalized, phi, derivative = param - return f'dim{dim}-s{s}-horizon{horizon}-normalized{normalized}-phi{phi}-derivative{derivative}' + for x, y in xy_values: + assert np.isclose(kernel(x, y), + kernel2(x, y)) + + boundaryKernel = kernel.getModifiedKernel(horizon=constant(np.inf)).getBoundaryKernel() + boundaryKernel2 = kernel2.getModifiedKernel(horizon=constant(np.inf)).getBoundaryKernel() + + assert boundaryKernel.is_integrable == boundaryKernel2.is_integrable + assert boundaryKernel.singularityValue == boundaryKernel2.singularityValue + + for x, y in xy_values: + np.testing.assert_allclose(boundaryKernel(x, y), + boundaryKernel2(x, y)) -from PyNucleus_fem import meshFactory, dofmapFactory mesh1d = meshFactory('interval', a=-1, b=1, hTarget=1e-2) dm1d = dofmapFactory('P1', mesh1d, -1) mesh2d = meshFactory('disc', hTarget=1e-1, n=8) dm2d = dofmapFactory('P1', mesh2d, -1) -@pytest.fixture(scope='module', params=[ - # 1d kernels - (1, fractionalOrderFactory('const', 0.25), np.inf, True, None, 0), - (1, fractionalOrderFactory('const', 0.25), np.inf, False, None, 0), - (1, fractionalOrderFactory('const', 0.25), 0.5, True, None, 0), - (1, fractionalOrderFactory('const', 0.25), 0.5, False, None, 0), - (1, fractionalOrderFactory('const', 0.75), np.inf, True, None, 0), - (1, fractionalOrderFactory('const', 0.75), np.inf, False, None, 0), - (1, fractionalOrderFactory('const', 0.75), 0.5, True, None, 0), - (1, fractionalOrderFactory('const', 0.75), 0.5, False, None, 0), - (1, fractionalOrderFactory('varconst', 0.25), np.inf, True, None, 0), - (1, fractionalOrderFactory('varconst', 0.25), np.inf, False, None, 0), - (1, fractionalOrderFactory('varconst', 0.25), 0.5, True, None, 0), - (1, fractionalOrderFactory('varconst', 0.25), 0.5, False, None, 0), - (1, fractionalOrderFactory('varconst', 0.75), np.inf, True, None, 0), - (1, fractionalOrderFactory('varconst', 0.75), np.inf, False, None, 0), - (1, fractionalOrderFactory('varconst', 0.75), 0.5, True, None, 0), - (1, fractionalOrderFactory('varconst', 0.75), 0.5, False, None, 0), - (1, fractionalOrderFactory('constantNonSym', 0.25), np.inf, True, None, 0), - (1, fractionalOrderFactory('constantNonSym', 0.25), np.inf, False, None, 0), - (1, fractionalOrderFactory('constantNonSym', 0.25), 0.5, True, None, 0), - (1, fractionalOrderFactory('constantNonSym', 0.25), 0.5, False, None, 0), - (1, fractionalOrderFactory('constantNonSym', 0.75), np.inf, True, None, 0), - (1, fractionalOrderFactory('constantNonSym', 0.75), np.inf, False, None, 0), - (1, fractionalOrderFactory('constantNonSym', 0.75), 0.5, True, None, 0), - (1, fractionalOrderFactory('constantNonSym', 0.75), 0.5, False, None, 0), - (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, True, None, 0), - (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, False, None, 0), - (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, True, None, 0), - (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, False, None, 0), - (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, True, None, 0), - (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, False, None, 0), - (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, True, None, 0), - (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, False, None, 0), - # discretized fractional order - (1, fractionalOrderFactory('const', 0.25, dm=dm1d), np.inf, True, None, 0), - (1, fractionalOrderFactory('const', 0.25, dm=dm1d), np.inf, False, None, 0), - (1, fractionalOrderFactory('const', 0.25, dm=dm1d), 0.5, True, None, 0), - (1, fractionalOrderFactory('const', 0.25, dm=dm1d), 0.5, False, None, 0), - (1, fractionalOrderFactory('const', 0.75, dm=dm1d), np.inf, True, None, 0), - (1, fractionalOrderFactory('const', 0.75, dm=dm1d), np.inf, False, None, 0), - (1, fractionalOrderFactory('const', 0.75, dm=dm1d), 0.5, True, None, 0), - (1, fractionalOrderFactory('const', 0.75, dm=dm1d), 0.5, False, None, 0), - (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25, dm=dm1d), np.inf, True, None, 0), - (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25, dm=dm1d), np.inf, False, None, 0), - (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25, dm=dm1d), 0.5, True, None, 0), - (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25, dm=dm1d), 0.5, False, None, 0), - (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75, dm=dm1d), np.inf, True, None, 0), - (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75, dm=dm1d), np.inf, False, None, 0), - (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75, dm=dm1d), 0.5, True, None, 0), - (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75, dm=dm1d), 0.5, False, None, 0), - # now with a trivial phi - (1, fractionalOrderFactory('const', 0.25), np.inf, True, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('const', 0.25), np.inf, False, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('const', 0.25), 0.5, True, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('const', 0.25), 0.5, False, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('const', 0.75), np.inf, True, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('const', 0.75), np.inf, False, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('const', 0.75), 0.5, True, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('const', 0.75), 0.5, False, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('constantSym', 0.25), np.inf, True, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('constantSym', 0.25), np.inf, False, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('constantSym', 0.25), 0.5, True, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('constantSym', 0.25), 0.5, False, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('constantSym', 0.75), np.inf, True, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('constantSym', 0.75), np.inf, False, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('constantSym', 0.75), 0.5, True, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('constantSym', 0.75), 0.5, False, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('constantNonSym', 0.25), np.inf, True, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('constantNonSym', 0.25), np.inf, False, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('constantNonSym', 0.25), 0.5, True, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('constantNonSym', 0.25), 0.5, False, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('constantNonSym', 0.75), np.inf, True, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('constantNonSym', 0.75), np.inf, False, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('constantNonSym', 0.75), 0.5, True, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('constantNonSym', 0.75), 0.5, False, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, True, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, False, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, True, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, False, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, True, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, False, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, True, twoPointFunctionFactory('const', 2.), 0), - (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, False, twoPointFunctionFactory('const', 2.), 0), - # first derivative wrt s - (1, fractionalOrderFactory('const', 0.25), np.inf, True, None, 1), - (1, fractionalOrderFactory('const', 0.25), np.inf, False, None, 1), - (1, fractionalOrderFactory('const', 0.25), 0.5, True, None, 1), - (1, fractionalOrderFactory('const', 0.25), 0.5, False, None, 1), - (1, fractionalOrderFactory('const', 0.75), np.inf, True, None, 1), - (1, fractionalOrderFactory('const', 0.75), np.inf, False, None, 1), - (1, fractionalOrderFactory('const', 0.75), 0.5, True, None, 1), - (1, fractionalOrderFactory('const', 0.75), 0.5, False, None, 1), - (1, fractionalOrderFactory('constantSym', 0.25), np.inf, True, None, 1), - (1, fractionalOrderFactory('constantSym', 0.25), np.inf, False, None, 1), - (1, fractionalOrderFactory('constantSym', 0.25), 0.5, True, None, 1), - (1, fractionalOrderFactory('constantSym', 0.25), 0.5, False, None, 1), - (1, fractionalOrderFactory('constantSym', 0.75), np.inf, True, None, 1), - (1, fractionalOrderFactory('constantSym', 0.75), np.inf, False, None, 1), - (1, fractionalOrderFactory('constantSym', 0.75), 0.5, True, None, 1), - (1, fractionalOrderFactory('constantSym', 0.75), 0.5, False, None, 1), - (1, fractionalOrderFactory('constantNonSym', 0.25), np.inf, True, None, 1), - (1, fractionalOrderFactory('constantNonSym', 0.25), np.inf, False, None, 1), - (1, fractionalOrderFactory('constantNonSym', 0.25), 0.5, True, None, 1), - (1, fractionalOrderFactory('constantNonSym', 0.25), 0.5, False, None, 1), - (1, fractionalOrderFactory('constantNonSym', 0.75), np.inf, True, None, 1), - (1, fractionalOrderFactory('constantNonSym', 0.75), np.inf, False, None, 1), - (1, fractionalOrderFactory('constantNonSym', 0.75), 0.5, True, None, 1), - (1, fractionalOrderFactory('constantNonSym', 0.75), 0.5, False, None, 1), - (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, True, None, 1), - (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, False, None, 1), - (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, True, None, 1), - (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, False, None, 1), - (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, True, None, 1), - (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, False, None, 1), - (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, True, None, 1), - (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, False, None, 1), - # second derivative wrt s - (1, fractionalOrderFactory('const', 0.25), np.inf, True, None, 2), - (1, fractionalOrderFactory('const', 0.25), np.inf, False, None, 2), - (1, fractionalOrderFactory('const', 0.25), 0.5, True, None, 2), - (1, fractionalOrderFactory('const', 0.25), 0.5, False, None, 2), - (1, fractionalOrderFactory('const', 0.75), np.inf, True, None, 2), - (1, fractionalOrderFactory('const', 0.75), np.inf, False, None, 2), - (1, fractionalOrderFactory('const', 0.75), 0.5, True, None, 2), - (1, fractionalOrderFactory('const', 0.75), 0.5, False, None, 2), - (1, fractionalOrderFactory('constantSym', 0.25), np.inf, True, None, 2), - (1, fractionalOrderFactory('constantSym', 0.25), np.inf, False, None, 2), - (1, fractionalOrderFactory('constantSym', 0.25), 0.5, True, None, 2), - (1, fractionalOrderFactory('constantSym', 0.25), 0.5, False, None, 2), - (1, fractionalOrderFactory('constantSym', 0.75), np.inf, True, None, 2), - (1, fractionalOrderFactory('constantSym', 0.75), np.inf, False, None, 2), - (1, fractionalOrderFactory('constantSym', 0.75), 0.5, True, None, 2), - (1, fractionalOrderFactory('constantSym', 0.75), 0.5, False, None, 2), - (1, fractionalOrderFactory('constantNonSym', 0.25), np.inf, True, None, 2), - (1, fractionalOrderFactory('constantNonSym', 0.25), np.inf, False, None, 2), - (1, fractionalOrderFactory('constantNonSym', 0.25), 0.5, True, None, 2), - (1, fractionalOrderFactory('constantNonSym', 0.25), 0.5, False, None, 2), - (1, fractionalOrderFactory('constantNonSym', 0.75), np.inf, True, None, 2), - (1, fractionalOrderFactory('constantNonSym', 0.75), np.inf, False, None, 2), - (1, fractionalOrderFactory('constantNonSym', 0.75), 0.5, True, None, 2), - (1, fractionalOrderFactory('constantNonSym', 0.75), 0.5, False, None, 2), - (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, True, None, 2), - (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, False, None, 2), - (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, True, None, 2), - (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, False, None, 2), - (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, True, None, 2), - (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, False, None, 2), - (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, True, None, 2), - (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, False, None, 2), - # discretized fractional order - (1, fractionalOrderFactory('const', 0.25, dm=dm1d), np.inf, True, None, 1), - (1, fractionalOrderFactory('const', 0.25, dm=dm1d), np.inf, False, None, 1), - (1, fractionalOrderFactory('const', 0.25, dm=dm1d), 0.5, True, None, 1), - (1, fractionalOrderFactory('const', 0.25, dm=dm1d), 0.5, False, None, 1), - (1, fractionalOrderFactory('const', 0.75, dm=dm1d), np.inf, True, None, 1), - (1, fractionalOrderFactory('const', 0.75, dm=dm1d), np.inf, False, None, 1), - (1, fractionalOrderFactory('const', 0.75, dm=dm1d), 0.5, True, None, 1), - (1, fractionalOrderFactory('const', 0.75, dm=dm1d), 0.5, False, None, 1), - (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25, dm=dm1d), np.inf, True, None, 1), - (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25, dm=dm1d), np.inf, False, None, 1), - (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25, dm=dm1d), 0.5, True, None, 1), - (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25, dm=dm1d), 0.5, False, None, 1), - (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75, dm=dm1d), np.inf, True, None, 1), - (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75, dm=dm1d), np.inf, False, None, 1), - (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75, dm=dm1d), 0.5, True, None, 1), - (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75, dm=dm1d), 0.5, False, None, 1), - ################################################## - # 2d kernels - (2, fractionalOrderFactory('const', 0.25), np.inf, True, None, 0), - (2, fractionalOrderFactory('const', 0.25), np.inf, False, None, 0), - (2, fractionalOrderFactory('const', 0.25), 0.5, True, None, 0), - (2, fractionalOrderFactory('const', 0.25), 0.5, False, None, 0), - (2, fractionalOrderFactory('const', 0.75), np.inf, True, None, 0), - (2, fractionalOrderFactory('const', 0.75), np.inf, False, None, 0), - (2, fractionalOrderFactory('const', 0.75), 0.5, True, None, 0), - (2, fractionalOrderFactory('const', 0.75), 0.5, False, None, 0), - (2, fractionalOrderFactory('constantSym', 0.25), np.inf, True, None, 0), - (2, fractionalOrderFactory('constantSym', 0.25), np.inf, False, None, 0), - (2, fractionalOrderFactory('constantSym', 0.25), 0.5, True, None, 0), - (2, fractionalOrderFactory('constantSym', 0.25), 0.5, False, None, 0), - (2, fractionalOrderFactory('constantSym', 0.75), np.inf, True, None, 0), - (2, fractionalOrderFactory('constantSym', 0.75), np.inf, False, None, 0), - (2, fractionalOrderFactory('constantSym', 0.75), 0.5, True, None, 0), - (2, fractionalOrderFactory('constantSym', 0.75), 0.5, False, None, 0), - (2, fractionalOrderFactory('constantNonSym', 0.25), np.inf, True, None, 0), - (2, fractionalOrderFactory('constantNonSym', 0.25), np.inf, False, None, 0), - (2, fractionalOrderFactory('constantNonSym', 0.25), 0.5, True, None, 0), - (2, fractionalOrderFactory('constantNonSym', 0.25), 0.5, False, None, 0), - (2, fractionalOrderFactory('constantNonSym', 0.75), np.inf, True, None, 0), - (2, fractionalOrderFactory('constantNonSym', 0.75), np.inf, False, None, 0), - (2, fractionalOrderFactory('constantNonSym', 0.75), 0.5, True, None, 0), - (2, fractionalOrderFactory('constantNonSym', 0.75), 0.5, False, None, 0), - (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, True, None, 0), - (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, False, None, 0), - (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, True, None, 0), - (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, False, None, 0), - (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, True, None, 0), - (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, False, None, 0), - (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, True, None, 0), - (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, False, None, 0), - # first derivative wrt s - (2, fractionalOrderFactory('const', 0.25), np.inf, True, None, 1), - (2, fractionalOrderFactory('const', 0.25), np.inf, False, None, 1), - (2, fractionalOrderFactory('const', 0.25), 0.5, True, None, 1), - (2, fractionalOrderFactory('const', 0.25), 0.5, False, None, 1), - (2, fractionalOrderFactory('const', 0.75), np.inf, True, None, 1), - (2, fractionalOrderFactory('const', 0.75), np.inf, False, None, 1), - (2, fractionalOrderFactory('const', 0.75), 0.5, True, None, 1), - (2, fractionalOrderFactory('const', 0.75), 0.5, False, None, 1), - (2, fractionalOrderFactory('constantSym', 0.25), np.inf, True, None, 1), - (2, fractionalOrderFactory('constantSym', 0.25), np.inf, False, None, 1), - (2, fractionalOrderFactory('constantSym', 0.25), 0.5, True, None, 1), - (2, fractionalOrderFactory('constantSym', 0.25), 0.5, False, None, 1), - (2, fractionalOrderFactory('constantSym', 0.75), np.inf, True, None, 1), - (2, fractionalOrderFactory('constantSym', 0.75), np.inf, False, None, 1), - (2, fractionalOrderFactory('constantSym', 0.75), 0.5, True, None, 1), - (2, fractionalOrderFactory('constantSym', 0.75), 0.5, False, None, 1), - (2, fractionalOrderFactory('constantNonSym', 0.25), np.inf, True, None, 1), - (2, fractionalOrderFactory('constantNonSym', 0.25), np.inf, False, None, 1), - (2, fractionalOrderFactory('constantNonSym', 0.25), 0.5, True, None, 1), - (2, fractionalOrderFactory('constantNonSym', 0.25), 0.5, False, None, 1), - (2, fractionalOrderFactory('constantNonSym', 0.75), np.inf, True, None, 1), - (2, fractionalOrderFactory('constantNonSym', 0.75), np.inf, False, None, 1), - (2, fractionalOrderFactory('constantNonSym', 0.75), 0.5, True, None, 1), - (2, fractionalOrderFactory('constantNonSym', 0.75), 0.5, False, None, 1), - (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, True, None, 1), - (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, False, None, 1), - (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, True, None, 1), - (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, False, None, 1), - (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, True, None, 1), - (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, False, None, 1), - (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, True, None, 1), - (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, False, None, 1), - # second derivative wrt s - (2, fractionalOrderFactory('const', 0.25), np.inf, True, None, 2), - (2, fractionalOrderFactory('const', 0.25), np.inf, False, None, 2), - (2, fractionalOrderFactory('const', 0.25), 0.5, True, None, 2), - (2, fractionalOrderFactory('const', 0.25), 0.5, False, None, 2), - (2, fractionalOrderFactory('const', 0.75), np.inf, True, None, 2), - (2, fractionalOrderFactory('const', 0.75), np.inf, False, None, 2), - (2, fractionalOrderFactory('const', 0.75), 0.5, True, None, 2), - (2, fractionalOrderFactory('const', 0.75), 0.5, False, None, 2), - (2, fractionalOrderFactory('constantSym', 0.25), np.inf, True, None, 2), - (2, fractionalOrderFactory('constantSym', 0.25), np.inf, False, None, 2), - (2, fractionalOrderFactory('constantSym', 0.25), 0.5, True, None, 2), - (2, fractionalOrderFactory('constantSym', 0.25), 0.5, False, None, 2), - (2, fractionalOrderFactory('constantSym', 0.75), np.inf, True, None, 2), - (2, fractionalOrderFactory('constantSym', 0.75), np.inf, False, None, 2), - (2, fractionalOrderFactory('constantSym', 0.75), 0.5, True, None, 2), - (2, fractionalOrderFactory('constantSym', 0.75), 0.5, False, None, 2), - (2, fractionalOrderFactory('constantNonSym', 0.25), np.inf, True, None, 2), - (2, fractionalOrderFactory('constantNonSym', 0.25), np.inf, False, None, 2), - (2, fractionalOrderFactory('constantNonSym', 0.25), 0.5, True, None, 2), - (2, fractionalOrderFactory('constantNonSym', 0.25), 0.5, False, None, 2), - (2, fractionalOrderFactory('constantNonSym', 0.75), np.inf, True, None, 2), - (2, fractionalOrderFactory('constantNonSym', 0.75), np.inf, False, None, 2), - (2, fractionalOrderFactory('constantNonSym', 0.75), 0.5, True, None, 2), - (2, fractionalOrderFactory('constantNonSym', 0.75), 0.5, False, None, 2), - (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, True, None, 2), - (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, False, None, 2), - (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, True, None, 2), - (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, False, None, 2), - (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, True, None, 2), - (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, False, None, 2), - (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, True, None, 2), - (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, False, None, 2), - # discretized fractional order - (2, fractionalOrderFactory('const', 0.25, dm=dm2d), np.inf, True, None, 0), - (2, fractionalOrderFactory('const', 0.25, dm=dm2d), np.inf, False, None, 0), - (2, fractionalOrderFactory('const', 0.25, dm=dm2d), 0.5, True, None, 0), - (2, fractionalOrderFactory('const', 0.25, dm=dm2d), 0.5, False, None, 0), - (2, fractionalOrderFactory('const', 0.75, dm=dm2d), np.inf, True, None, 0), - (2, fractionalOrderFactory('const', 0.75, dm=dm2d), np.inf, False, None, 0), - (2, fractionalOrderFactory('const', 0.75, dm=dm2d), 0.5, True, None, 0), - (2, fractionalOrderFactory('const', 0.75, dm=dm2d), 0.5, False, None, 0), - (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25, dm=dm2d), np.inf, True, None, 0), - (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25, dm=dm2d), np.inf, False, None, 0), - (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25, dm=dm2d), 0.5, True, None, 0), - (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25, dm=dm2d), 0.5, False, None, 0), - (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75, dm=dm2d), np.inf, True, None, 0), - (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75, dm=dm2d), np.inf, False, None, 0), - (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75, dm=dm2d), 0.5, True, None, 0), - (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75, dm=dm2d), 0.5, False, None, 0), - ################################################## - # 3d kernels - (3, fractionalOrderFactory('const', 0.25), np.inf, True, None, 0), - (3, fractionalOrderFactory('const', 0.25), np.inf, False, None, 0), - (3, fractionalOrderFactory('const', 0.25), 0.5, True, None, 0), - (3, fractionalOrderFactory('const', 0.25), 0.5, False, None, 0), - (3, fractionalOrderFactory('const', 0.75), np.inf, True, None, 0), - (3, fractionalOrderFactory('const', 0.75), np.inf, False, None, 0), - (3, fractionalOrderFactory('const', 0.75), 0.5, True, None, 0), - (3, fractionalOrderFactory('const', 0.75), 0.5, False, None, 0), - (3, fractionalOrderFactory('constantSym', 0.25), np.inf, True, None, 0), - (3, fractionalOrderFactory('constantSym', 0.25), np.inf, False, None, 0), - (3, fractionalOrderFactory('constantSym', 0.25), 0.5, True, None, 0), - (3, fractionalOrderFactory('constantSym', 0.25), 0.5, False, None, 0), - (3, fractionalOrderFactory('constantSym', 0.75), np.inf, True, None, 0), - (3, fractionalOrderFactory('constantSym', 0.75), np.inf, False, None, 0), - (3, fractionalOrderFactory('constantSym', 0.75), 0.5, True, None, 0), - (3, fractionalOrderFactory('constantSym', 0.75), 0.5, False, None, 0), - (3, fractionalOrderFactory('constantNonSym', 0.25), np.inf, True, None, 0), - (3, fractionalOrderFactory('constantNonSym', 0.25), np.inf, False, None, 0), - (3, fractionalOrderFactory('constantNonSym', 0.25), 0.5, True, None, 0), - (3, fractionalOrderFactory('constantNonSym', 0.25), 0.5, False, None, 0), - (3, fractionalOrderFactory('constantNonSym', 0.75), np.inf, True, None, 0), - (3, fractionalOrderFactory('constantNonSym', 0.75), np.inf, False, None, 0), - (3, fractionalOrderFactory('constantNonSym', 0.75), 0.5, True, None, 0), - (3, fractionalOrderFactory('constantNonSym', 0.75), 0.5, False, None, 0), - (3, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, True, None, 0), - (3, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, False, None, 0), - (3, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, True, None, 0), - (3, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, False, None, 0), - (3, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, True, None, 0), - (3, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, False, None, 0), - (3, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, True, None, 0), - (3, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, False, None, 0), - # first derivative wrt s - (3, fractionalOrderFactory('const', 0.25), np.inf, True, None, 1), - (3, fractionalOrderFactory('const', 0.25), np.inf, False, None, 1), - (3, fractionalOrderFactory('const', 0.25), 0.5, True, None, 1), - (3, fractionalOrderFactory('const', 0.25), 0.5, False, None, 1), - (3, fractionalOrderFactory('const', 0.75), np.inf, True, None, 1), - (3, fractionalOrderFactory('const', 0.75), np.inf, False, None, 1), - (3, fractionalOrderFactory('const', 0.75), 0.5, True, None, 1), - (3, fractionalOrderFactory('const', 0.75), 0.5, False, None, 1), - (3, fractionalOrderFactory('constantSym', 0.25), np.inf, True, None, 1), - (3, fractionalOrderFactory('constantSym', 0.25), np.inf, False, None, 1), - (3, fractionalOrderFactory('constantSym', 0.25), 0.5, True, None, 1), - (3, fractionalOrderFactory('constantSym', 0.25), 0.5, False, None, 1), - (3, fractionalOrderFactory('constantSym', 0.75), np.inf, True, None, 1), - (3, fractionalOrderFactory('constantSym', 0.75), np.inf, False, None, 1), - (3, fractionalOrderFactory('constantSym', 0.75), 0.5, True, None, 1), - (3, fractionalOrderFactory('constantSym', 0.75), 0.5, False, None, 1), - (3, fractionalOrderFactory('constantNonSym', 0.25), np.inf, True, None, 1), - (3, fractionalOrderFactory('constantNonSym', 0.25), np.inf, False, None, 1), - (3, fractionalOrderFactory('constantNonSym', 0.25), 0.5, True, None, 1), - (3, fractionalOrderFactory('constantNonSym', 0.25), 0.5, False, None, 1), - (3, fractionalOrderFactory('constantNonSym', 0.75), np.inf, True, None, 1), - (3, fractionalOrderFactory('constantNonSym', 0.75), np.inf, False, None, 1), - (3, fractionalOrderFactory('constantNonSym', 0.75), 0.5, True, None, 1), - (3, fractionalOrderFactory('constantNonSym', 0.75), 0.5, False, None, 1), - (3, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, True, None, 1), - (3, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, False, None, 1), - (3, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, True, None, 1), - (3, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, False, None, 1), - (3, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, True, None, 1), - (3, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, False, None, 1), - (3, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, True, None, 1), - (3, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, False, None, 1), - # second derivative wrt s - (3, fractionalOrderFactory('const', 0.25), np.inf, True, None, 2), - (3, fractionalOrderFactory('const', 0.25), np.inf, False, None, 2), - (3, fractionalOrderFactory('const', 0.25), 0.5, True, None, 2), - (3, fractionalOrderFactory('const', 0.25), 0.5, False, None, 2), - (3, fractionalOrderFactory('const', 0.75), np.inf, True, None, 2), - (3, fractionalOrderFactory('const', 0.75), np.inf, False, None, 2), - (3, fractionalOrderFactory('const', 0.75), 0.5, True, None, 2), - (3, fractionalOrderFactory('const', 0.75), 0.5, False, None, 2), - (3, fractionalOrderFactory('constantSym', 0.25), np.inf, True, None, 2), - (3, fractionalOrderFactory('constantSym', 0.25), np.inf, False, None, 2), - (3, fractionalOrderFactory('constantSym', 0.25), 0.5, True, None, 2), - (3, fractionalOrderFactory('constantSym', 0.25), 0.5, False, None, 2), - (3, fractionalOrderFactory('constantSym', 0.75), np.inf, True, None, 2), - (3, fractionalOrderFactory('constantSym', 0.75), np.inf, False, None, 2), - (3, fractionalOrderFactory('constantSym', 0.75), 0.5, True, None, 2), - (3, fractionalOrderFactory('constantSym', 0.75), 0.5, False, None, 2), - (3, fractionalOrderFactory('constantNonSym', 0.25), np.inf, True, None, 2), - (3, fractionalOrderFactory('constantNonSym', 0.25), np.inf, False, None, 2), - (3, fractionalOrderFactory('constantNonSym', 0.25), 0.5, True, None, 2), - (3, fractionalOrderFactory('constantNonSym', 0.25), 0.5, False, None, 2), - (3, fractionalOrderFactory('constantNonSym', 0.75), np.inf, True, None, 2), - (3, fractionalOrderFactory('constantNonSym', 0.75), np.inf, False, None, 2), - (3, fractionalOrderFactory('constantNonSym', 0.75), 0.5, True, None, 2), - (3, fractionalOrderFactory('constantNonSym', 0.75), 0.5, False, None, 2), - (3, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, True, None, 2), - (3, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, False, None, 2), - (3, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, True, None, 2), - (3, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, False, None, 2), - (3, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, True, None, 2), - (3, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, False, None, 2), - (3, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, True, None, 2), - (3, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, False, None, 2), -], ids=idfuncFractional) -def fractionalKernelParams(request): - return request.param - - -def testFractionalKernel(fractionalKernelParams): - dim, s, horizon, normalized, phi, derivative = fractionalKernelParams +def idfunc(arg): + return repr(arg) + + +@pytest.mark.parametrize( + "dim, s, horizon, normalized, phi, derivative", + [ + # 1d kernels + (1, fractionalOrderFactory('const', 0.25), np.inf, True, None, 0), + (1, fractionalOrderFactory('const', 0.25), np.inf, False, None, 0), + (1, fractionalOrderFactory('const', 0.25), 0.5, True, None, 0), + (1, fractionalOrderFactory('const', 0.25), 0.5, False, None, 0), + (1, fractionalOrderFactory('const', 0.75), np.inf, True, None, 0), + (1, fractionalOrderFactory('const', 0.75), np.inf, False, None, 0), + (1, fractionalOrderFactory('const', 0.75), 0.5, True, None, 0), + (1, fractionalOrderFactory('const', 0.75), 0.5, False, None, 0), + (1, fractionalOrderFactory('varconst', 0.25), np.inf, True, None, 0), + (1, fractionalOrderFactory('varconst', 0.25), np.inf, False, None, 0), + (1, fractionalOrderFactory('varconst', 0.25), 0.5, True, None, 0), + (1, fractionalOrderFactory('varconst', 0.25), 0.5, False, None, 0), + (1, fractionalOrderFactory('varconst', 0.75), np.inf, True, None, 0), + (1, fractionalOrderFactory('varconst', 0.75), np.inf, False, None, 0), + (1, fractionalOrderFactory('varconst', 0.75), 0.5, True, None, 0), + (1, fractionalOrderFactory('varconst', 0.75), 0.5, False, None, 0), + (1, fractionalOrderFactory('constantNonSym', 0.25), np.inf, True, None, 0), + (1, fractionalOrderFactory('constantNonSym', 0.25), np.inf, False, None, 0), + (1, fractionalOrderFactory('constantNonSym', 0.25), 0.5, True, None, 0), + (1, fractionalOrderFactory('constantNonSym', 0.25), 0.5, False, None, 0), + (1, fractionalOrderFactory('constantNonSym', 0.75), np.inf, True, None, 0), + (1, fractionalOrderFactory('constantNonSym', 0.75), np.inf, False, None, 0), + (1, fractionalOrderFactory('constantNonSym', 0.75), 0.5, True, None, 0), + (1, fractionalOrderFactory('constantNonSym', 0.75), 0.5, False, None, 0), + (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, True, None, 0), + (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, False, None, 0), + (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, True, None, 0), + (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, False, None, 0), + (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, True, None, 0), + (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, False, None, 0), + (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, True, None, 0), + (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, False, None, 0), + # discretized fractional order + (1, fractionalOrderFactory('const', 0.25, dm=dm1d), np.inf, True, None, 0), + (1, fractionalOrderFactory('const', 0.25, dm=dm1d), np.inf, False, None, 0), + (1, fractionalOrderFactory('const', 0.25, dm=dm1d), 0.5, True, None, 0), + (1, fractionalOrderFactory('const', 0.25, dm=dm1d), 0.5, False, None, 0), + (1, fractionalOrderFactory('const', 0.75, dm=dm1d), np.inf, True, None, 0), + (1, fractionalOrderFactory('const', 0.75, dm=dm1d), np.inf, False, None, 0), + (1, fractionalOrderFactory('const', 0.75, dm=dm1d), 0.5, True, None, 0), + (1, fractionalOrderFactory('const', 0.75, dm=dm1d), 0.5, False, None, 0), + (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25, dm=dm1d), np.inf, True, None, 0), + (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25, dm=dm1d), np.inf, False, None, 0), + (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25, dm=dm1d), 0.5, True, None, 0), + (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25, dm=dm1d), 0.5, False, None, 0), + (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75, dm=dm1d), np.inf, True, None, 0), + (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75, dm=dm1d), np.inf, False, None, 0), + (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75, dm=dm1d), 0.5, True, None, 0), + (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75, dm=dm1d), 0.5, False, None, 0), + # now with a trivial phi + (1, fractionalOrderFactory('const', 0.25), np.inf, True, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('const', 0.25), np.inf, False, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('const', 0.25), 0.5, True, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('const', 0.25), 0.5, False, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('const', 0.75), np.inf, True, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('const', 0.75), np.inf, False, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('const', 0.75), 0.5, True, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('const', 0.75), 0.5, False, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('constantSym', 0.25), np.inf, True, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('constantSym', 0.25), np.inf, False, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('constantSym', 0.25), 0.5, True, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('constantSym', 0.25), 0.5, False, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('constantSym', 0.75), np.inf, True, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('constantSym', 0.75), np.inf, False, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('constantSym', 0.75), 0.5, True, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('constantSym', 0.75), 0.5, False, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('constantNonSym', 0.25), np.inf, True, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('constantNonSym', 0.25), np.inf, False, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('constantNonSym', 0.25), 0.5, True, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('constantNonSym', 0.25), 0.5, False, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('constantNonSym', 0.75), np.inf, True, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('constantNonSym', 0.75), np.inf, False, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('constantNonSym', 0.75), 0.5, True, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('constantNonSym', 0.75), 0.5, False, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, True, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, False, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, True, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, False, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, True, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, False, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, True, twoPointFunctionFactory('const', 2.), 0), + (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, False, twoPointFunctionFactory('const', 2.), 0), + # first derivative wrt s + (1, fractionalOrderFactory('const', 0.25), np.inf, True, None, 1), + (1, fractionalOrderFactory('const', 0.25), np.inf, False, None, 1), + (1, fractionalOrderFactory('const', 0.25), 0.5, True, None, 1), + (1, fractionalOrderFactory('const', 0.25), 0.5, False, None, 1), + (1, fractionalOrderFactory('const', 0.75), np.inf, True, None, 1), + (1, fractionalOrderFactory('const', 0.75), np.inf, False, None, 1), + (1, fractionalOrderFactory('const', 0.75), 0.5, True, None, 1), + (1, fractionalOrderFactory('const', 0.75), 0.5, False, None, 1), + (1, fractionalOrderFactory('constantSym', 0.25), np.inf, True, None, 1), + (1, fractionalOrderFactory('constantSym', 0.25), np.inf, False, None, 1), + (1, fractionalOrderFactory('constantSym', 0.25), 0.5, True, None, 1), + (1, fractionalOrderFactory('constantSym', 0.25), 0.5, False, None, 1), + (1, fractionalOrderFactory('constantSym', 0.75), np.inf, True, None, 1), + (1, fractionalOrderFactory('constantSym', 0.75), np.inf, False, None, 1), + (1, fractionalOrderFactory('constantSym', 0.75), 0.5, True, None, 1), + (1, fractionalOrderFactory('constantSym', 0.75), 0.5, False, None, 1), + (1, fractionalOrderFactory('constantNonSym', 0.25), np.inf, True, None, 1), + (1, fractionalOrderFactory('constantNonSym', 0.25), np.inf, False, None, 1), + (1, fractionalOrderFactory('constantNonSym', 0.25), 0.5, True, None, 1), + (1, fractionalOrderFactory('constantNonSym', 0.25), 0.5, False, None, 1), + (1, fractionalOrderFactory('constantNonSym', 0.75), np.inf, True, None, 1), + (1, fractionalOrderFactory('constantNonSym', 0.75), np.inf, False, None, 1), + (1, fractionalOrderFactory('constantNonSym', 0.75), 0.5, True, None, 1), + (1, fractionalOrderFactory('constantNonSym', 0.75), 0.5, False, None, 1), + (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, True, None, 1), + (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, False, None, 1), + (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, True, None, 1), + (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, False, None, 1), + (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, True, None, 1), + (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, False, None, 1), + (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, True, None, 1), + (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, False, None, 1), + # second derivative wrt s + (1, fractionalOrderFactory('const', 0.25), np.inf, True, None, 2), + (1, fractionalOrderFactory('const', 0.25), np.inf, False, None, 2), + (1, fractionalOrderFactory('const', 0.25), 0.5, True, None, 2), + (1, fractionalOrderFactory('const', 0.25), 0.5, False, None, 2), + (1, fractionalOrderFactory('const', 0.75), np.inf, True, None, 2), + (1, fractionalOrderFactory('const', 0.75), np.inf, False, None, 2), + (1, fractionalOrderFactory('const', 0.75), 0.5, True, None, 2), + (1, fractionalOrderFactory('const', 0.75), 0.5, False, None, 2), + (1, fractionalOrderFactory('constantSym', 0.25), np.inf, True, None, 2), + (1, fractionalOrderFactory('constantSym', 0.25), np.inf, False, None, 2), + (1, fractionalOrderFactory('constantSym', 0.25), 0.5, True, None, 2), + (1, fractionalOrderFactory('constantSym', 0.25), 0.5, False, None, 2), + (1, fractionalOrderFactory('constantSym', 0.75), np.inf, True, None, 2), + (1, fractionalOrderFactory('constantSym', 0.75), np.inf, False, None, 2), + (1, fractionalOrderFactory('constantSym', 0.75), 0.5, True, None, 2), + (1, fractionalOrderFactory('constantSym', 0.75), 0.5, False, None, 2), + (1, fractionalOrderFactory('constantNonSym', 0.25), np.inf, True, None, 2), + (1, fractionalOrderFactory('constantNonSym', 0.25), np.inf, False, None, 2), + (1, fractionalOrderFactory('constantNonSym', 0.25), 0.5, True, None, 2), + (1, fractionalOrderFactory('constantNonSym', 0.25), 0.5, False, None, 2), + (1, fractionalOrderFactory('constantNonSym', 0.75), np.inf, True, None, 2), + (1, fractionalOrderFactory('constantNonSym', 0.75), np.inf, False, None, 2), + (1, fractionalOrderFactory('constantNonSym', 0.75), 0.5, True, None, 2), + (1, fractionalOrderFactory('constantNonSym', 0.75), 0.5, False, None, 2), + (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, True, None, 2), + (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, False, None, 2), + (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, True, None, 2), + (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, False, None, 2), + (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, True, None, 2), + (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, False, None, 2), + (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, True, None, 2), + (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, False, None, 2), + # discretized fractional order + (1, fractionalOrderFactory('const', 0.25, dm=dm1d), np.inf, True, None, 1), + (1, fractionalOrderFactory('const', 0.25, dm=dm1d), np.inf, False, None, 1), + (1, fractionalOrderFactory('const', 0.25, dm=dm1d), 0.5, True, None, 1), + (1, fractionalOrderFactory('const', 0.25, dm=dm1d), 0.5, False, None, 1), + (1, fractionalOrderFactory('const', 0.75, dm=dm1d), np.inf, True, None, 1), + (1, fractionalOrderFactory('const', 0.75, dm=dm1d), np.inf, False, None, 1), + (1, fractionalOrderFactory('const', 0.75, dm=dm1d), 0.5, True, None, 1), + (1, fractionalOrderFactory('const', 0.75, dm=dm1d), 0.5, False, None, 1), + (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25, dm=dm1d), np.inf, True, None, 1), + (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25, dm=dm1d), np.inf, False, None, 1), + (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25, dm=dm1d), 0.5, True, None, 1), + (1, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25, dm=dm1d), 0.5, False, None, 1), + (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75, dm=dm1d), np.inf, True, None, 1), + (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75, dm=dm1d), np.inf, False, None, 1), + (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75, dm=dm1d), 0.5, True, None, 1), + (1, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75, dm=dm1d), 0.5, False, None, 1), + ################################################## + # 2d kernels + (2, fractionalOrderFactory('const', 0.25), np.inf, True, None, 0), + (2, fractionalOrderFactory('const', 0.25), np.inf, False, None, 0), + (2, fractionalOrderFactory('const', 0.25), 0.5, True, None, 0), + (2, fractionalOrderFactory('const', 0.25), 0.5, False, None, 0), + (2, fractionalOrderFactory('const', 0.75), np.inf, True, None, 0), + (2, fractionalOrderFactory('const', 0.75), np.inf, False, None, 0), + (2, fractionalOrderFactory('const', 0.75), 0.5, True, None, 0), + (2, fractionalOrderFactory('const', 0.75), 0.5, False, None, 0), + (2, fractionalOrderFactory('constantSym', 0.25), np.inf, True, None, 0), + (2, fractionalOrderFactory('constantSym', 0.25), np.inf, False, None, 0), + (2, fractionalOrderFactory('constantSym', 0.25), 0.5, True, None, 0), + (2, fractionalOrderFactory('constantSym', 0.25), 0.5, False, None, 0), + (2, fractionalOrderFactory('constantSym', 0.75), np.inf, True, None, 0), + (2, fractionalOrderFactory('constantSym', 0.75), np.inf, False, None, 0), + (2, fractionalOrderFactory('constantSym', 0.75), 0.5, True, None, 0), + (2, fractionalOrderFactory('constantSym', 0.75), 0.5, False, None, 0), + (2, fractionalOrderFactory('constantNonSym', 0.25), np.inf, True, None, 0), + (2, fractionalOrderFactory('constantNonSym', 0.25), np.inf, False, None, 0), + (2, fractionalOrderFactory('constantNonSym', 0.25), 0.5, True, None, 0), + (2, fractionalOrderFactory('constantNonSym', 0.25), 0.5, False, None, 0), + (2, fractionalOrderFactory('constantNonSym', 0.75), np.inf, True, None, 0), + (2, fractionalOrderFactory('constantNonSym', 0.75), np.inf, False, None, 0), + (2, fractionalOrderFactory('constantNonSym', 0.75), 0.5, True, None, 0), + (2, fractionalOrderFactory('constantNonSym', 0.75), 0.5, False, None, 0), + (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, True, None, 0), + (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, False, None, 0), + (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, True, None, 0), + (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, False, None, 0), + (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, True, None, 0), + (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, False, None, 0), + (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, True, None, 0), + (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, False, None, 0), + # first derivative wrt s + (2, fractionalOrderFactory('const', 0.25), np.inf, True, None, 1), + (2, fractionalOrderFactory('const', 0.25), np.inf, False, None, 1), + (2, fractionalOrderFactory('const', 0.25), 0.5, True, None, 1), + (2, fractionalOrderFactory('const', 0.25), 0.5, False, None, 1), + (2, fractionalOrderFactory('const', 0.75), np.inf, True, None, 1), + (2, fractionalOrderFactory('const', 0.75), np.inf, False, None, 1), + (2, fractionalOrderFactory('const', 0.75), 0.5, True, None, 1), + (2, fractionalOrderFactory('const', 0.75), 0.5, False, None, 1), + (2, fractionalOrderFactory('constantSym', 0.25), np.inf, True, None, 1), + (2, fractionalOrderFactory('constantSym', 0.25), np.inf, False, None, 1), + (2, fractionalOrderFactory('constantSym', 0.25), 0.5, True, None, 1), + (2, fractionalOrderFactory('constantSym', 0.25), 0.5, False, None, 1), + (2, fractionalOrderFactory('constantSym', 0.75), np.inf, True, None, 1), + (2, fractionalOrderFactory('constantSym', 0.75), np.inf, False, None, 1), + (2, fractionalOrderFactory('constantSym', 0.75), 0.5, True, None, 1), + (2, fractionalOrderFactory('constantSym', 0.75), 0.5, False, None, 1), + (2, fractionalOrderFactory('constantNonSym', 0.25), np.inf, True, None, 1), + (2, fractionalOrderFactory('constantNonSym', 0.25), np.inf, False, None, 1), + (2, fractionalOrderFactory('constantNonSym', 0.25), 0.5, True, None, 1), + (2, fractionalOrderFactory('constantNonSym', 0.25), 0.5, False, None, 1), + (2, fractionalOrderFactory('constantNonSym', 0.75), np.inf, True, None, 1), + (2, fractionalOrderFactory('constantNonSym', 0.75), np.inf, False, None, 1), + (2, fractionalOrderFactory('constantNonSym', 0.75), 0.5, True, None, 1), + (2, fractionalOrderFactory('constantNonSym', 0.75), 0.5, False, None, 1), + (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, True, None, 1), + (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, False, None, 1), + (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, True, None, 1), + (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, False, None, 1), + (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, True, None, 1), + (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, False, None, 1), + (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, True, None, 1), + (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, False, None, 1), + # second derivative wrt s + (2, fractionalOrderFactory('const', 0.25), np.inf, True, None, 2), + (2, fractionalOrderFactory('const', 0.25), np.inf, False, None, 2), + (2, fractionalOrderFactory('const', 0.25), 0.5, True, None, 2), + (2, fractionalOrderFactory('const', 0.25), 0.5, False, None, 2), + (2, fractionalOrderFactory('const', 0.75), np.inf, True, None, 2), + (2, fractionalOrderFactory('const', 0.75), np.inf, False, None, 2), + (2, fractionalOrderFactory('const', 0.75), 0.5, True, None, 2), + (2, fractionalOrderFactory('const', 0.75), 0.5, False, None, 2), + (2, fractionalOrderFactory('constantSym', 0.25), np.inf, True, None, 2), + (2, fractionalOrderFactory('constantSym', 0.25), np.inf, False, None, 2), + (2, fractionalOrderFactory('constantSym', 0.25), 0.5, True, None, 2), + (2, fractionalOrderFactory('constantSym', 0.25), 0.5, False, None, 2), + (2, fractionalOrderFactory('constantSym', 0.75), np.inf, True, None, 2), + (2, fractionalOrderFactory('constantSym', 0.75), np.inf, False, None, 2), + (2, fractionalOrderFactory('constantSym', 0.75), 0.5, True, None, 2), + (2, fractionalOrderFactory('constantSym', 0.75), 0.5, False, None, 2), + (2, fractionalOrderFactory('constantNonSym', 0.25), np.inf, True, None, 2), + (2, fractionalOrderFactory('constantNonSym', 0.25), np.inf, False, None, 2), + (2, fractionalOrderFactory('constantNonSym', 0.25), 0.5, True, None, 2), + (2, fractionalOrderFactory('constantNonSym', 0.25), 0.5, False, None, 2), + (2, fractionalOrderFactory('constantNonSym', 0.75), np.inf, True, None, 2), + (2, fractionalOrderFactory('constantNonSym', 0.75), np.inf, False, None, 2), + (2, fractionalOrderFactory('constantNonSym', 0.75), 0.5, True, None, 2), + (2, fractionalOrderFactory('constantNonSym', 0.75), 0.5, False, None, 2), + (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, True, None, 2), + (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, False, None, 2), + (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, True, None, 2), + (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, False, None, 2), + (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, True, None, 2), + (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, False, None, 2), + (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, True, None, 2), + (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, False, None, 2), + # discretized fractional order + (2, fractionalOrderFactory('const', 0.25, dm=dm2d), np.inf, True, None, 0), + (2, fractionalOrderFactory('const', 0.25, dm=dm2d), np.inf, False, None, 0), + (2, fractionalOrderFactory('const', 0.25, dm=dm2d), 0.5, True, None, 0), + (2, fractionalOrderFactory('const', 0.25, dm=dm2d), 0.5, False, None, 0), + (2, fractionalOrderFactory('const', 0.75, dm=dm2d), np.inf, True, None, 0), + (2, fractionalOrderFactory('const', 0.75, dm=dm2d), np.inf, False, None, 0), + (2, fractionalOrderFactory('const', 0.75, dm=dm2d), 0.5, True, None, 0), + (2, fractionalOrderFactory('const', 0.75, dm=dm2d), 0.5, False, None, 0), + (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25, dm=dm2d), np.inf, True, None, 0), + (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25, dm=dm2d), np.inf, False, None, 0), + (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25, dm=dm2d), 0.5, True, None, 0), + (2, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25, dm=dm2d), 0.5, False, None, 0), + (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75, dm=dm2d), np.inf, True, None, 0), + (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75, dm=dm2d), np.inf, False, None, 0), + (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75, dm=dm2d), 0.5, True, None, 0), + (2, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75, dm=dm2d), 0.5, False, None, 0), + ################################################## + # 3d kernels + (3, fractionalOrderFactory('const', 0.25), np.inf, True, None, 0), + (3, fractionalOrderFactory('const', 0.25), np.inf, False, None, 0), + (3, fractionalOrderFactory('const', 0.25), 0.5, True, None, 0), + (3, fractionalOrderFactory('const', 0.25), 0.5, False, None, 0), + (3, fractionalOrderFactory('const', 0.75), np.inf, True, None, 0), + (3, fractionalOrderFactory('const', 0.75), np.inf, False, None, 0), + (3, fractionalOrderFactory('const', 0.75), 0.5, True, None, 0), + (3, fractionalOrderFactory('const', 0.75), 0.5, False, None, 0), + (3, fractionalOrderFactory('constantSym', 0.25), np.inf, True, None, 0), + (3, fractionalOrderFactory('constantSym', 0.25), np.inf, False, None, 0), + (3, fractionalOrderFactory('constantSym', 0.25), 0.5, True, None, 0), + (3, fractionalOrderFactory('constantSym', 0.25), 0.5, False, None, 0), + (3, fractionalOrderFactory('constantSym', 0.75), np.inf, True, None, 0), + (3, fractionalOrderFactory('constantSym', 0.75), np.inf, False, None, 0), + (3, fractionalOrderFactory('constantSym', 0.75), 0.5, True, None, 0), + (3, fractionalOrderFactory('constantSym', 0.75), 0.5, False, None, 0), + (3, fractionalOrderFactory('constantNonSym', 0.25), np.inf, True, None, 0), + (3, fractionalOrderFactory('constantNonSym', 0.25), np.inf, False, None, 0), + (3, fractionalOrderFactory('constantNonSym', 0.25), 0.5, True, None, 0), + (3, fractionalOrderFactory('constantNonSym', 0.25), 0.5, False, None, 0), + (3, fractionalOrderFactory('constantNonSym', 0.75), np.inf, True, None, 0), + (3, fractionalOrderFactory('constantNonSym', 0.75), np.inf, False, None, 0), + (3, fractionalOrderFactory('constantNonSym', 0.75), 0.5, True, None, 0), + (3, fractionalOrderFactory('constantNonSym', 0.75), 0.5, False, None, 0), + (3, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, True, None, 0), + (3, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, False, None, 0), + (3, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, True, None, 0), + (3, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, False, None, 0), + (3, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, True, None, 0), + (3, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, False, None, 0), + (3, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, True, None, 0), + (3, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, False, None, 0), + # first derivative wrt s + (3, fractionalOrderFactory('const', 0.25), np.inf, True, None, 1), + (3, fractionalOrderFactory('const', 0.25), np.inf, False, None, 1), + (3, fractionalOrderFactory('const', 0.25), 0.5, True, None, 1), + (3, fractionalOrderFactory('const', 0.25), 0.5, False, None, 1), + (3, fractionalOrderFactory('const', 0.75), np.inf, True, None, 1), + (3, fractionalOrderFactory('const', 0.75), np.inf, False, None, 1), + (3, fractionalOrderFactory('const', 0.75), 0.5, True, None, 1), + (3, fractionalOrderFactory('const', 0.75), 0.5, False, None, 1), + (3, fractionalOrderFactory('constantSym', 0.25), np.inf, True, None, 1), + (3, fractionalOrderFactory('constantSym', 0.25), np.inf, False, None, 1), + (3, fractionalOrderFactory('constantSym', 0.25), 0.5, True, None, 1), + (3, fractionalOrderFactory('constantSym', 0.25), 0.5, False, None, 1), + (3, fractionalOrderFactory('constantSym', 0.75), np.inf, True, None, 1), + (3, fractionalOrderFactory('constantSym', 0.75), np.inf, False, None, 1), + (3, fractionalOrderFactory('constantSym', 0.75), 0.5, True, None, 1), + (3, fractionalOrderFactory('constantSym', 0.75), 0.5, False, None, 1), + (3, fractionalOrderFactory('constantNonSym', 0.25), np.inf, True, None, 1), + (3, fractionalOrderFactory('constantNonSym', 0.25), np.inf, False, None, 1), + (3, fractionalOrderFactory('constantNonSym', 0.25), 0.5, True, None, 1), + (3, fractionalOrderFactory('constantNonSym', 0.25), 0.5, False, None, 1), + (3, fractionalOrderFactory('constantNonSym', 0.75), np.inf, True, None, 1), + (3, fractionalOrderFactory('constantNonSym', 0.75), np.inf, False, None, 1), + (3, fractionalOrderFactory('constantNonSym', 0.75), 0.5, True, None, 1), + (3, fractionalOrderFactory('constantNonSym', 0.75), 0.5, False, None, 1), + (3, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, True, None, 1), + (3, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, False, None, 1), + (3, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, True, None, 1), + (3, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, False, None, 1), + (3, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, True, None, 1), + (3, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, False, None, 1), + (3, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, True, None, 1), + (3, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, False, None, 1), + # second derivative wrt s + (3, fractionalOrderFactory('const', 0.25), np.inf, True, None, 2), + (3, fractionalOrderFactory('const', 0.25), np.inf, False, None, 2), + (3, fractionalOrderFactory('const', 0.25), 0.5, True, None, 2), + (3, fractionalOrderFactory('const', 0.25), 0.5, False, None, 2), + (3, fractionalOrderFactory('const', 0.75), np.inf, True, None, 2), + (3, fractionalOrderFactory('const', 0.75), np.inf, False, None, 2), + (3, fractionalOrderFactory('const', 0.75), 0.5, True, None, 2), + (3, fractionalOrderFactory('const', 0.75), 0.5, False, None, 2), + (3, fractionalOrderFactory('constantSym', 0.25), np.inf, True, None, 2), + (3, fractionalOrderFactory('constantSym', 0.25), np.inf, False, None, 2), + (3, fractionalOrderFactory('constantSym', 0.25), 0.5, True, None, 2), + (3, fractionalOrderFactory('constantSym', 0.25), 0.5, False, None, 2), + (3, fractionalOrderFactory('constantSym', 0.75), np.inf, True, None, 2), + (3, fractionalOrderFactory('constantSym', 0.75), np.inf, False, None, 2), + (3, fractionalOrderFactory('constantSym', 0.75), 0.5, True, None, 2), + (3, fractionalOrderFactory('constantSym', 0.75), 0.5, False, None, 2), + (3, fractionalOrderFactory('constantNonSym', 0.25), np.inf, True, None, 2), + (3, fractionalOrderFactory('constantNonSym', 0.25), np.inf, False, None, 2), + (3, fractionalOrderFactory('constantNonSym', 0.25), 0.5, True, None, 2), + (3, fractionalOrderFactory('constantNonSym', 0.25), 0.5, False, None, 2), + (3, fractionalOrderFactory('constantNonSym', 0.75), np.inf, True, None, 2), + (3, fractionalOrderFactory('constantNonSym', 0.75), np.inf, False, None, 2), + (3, fractionalOrderFactory('constantNonSym', 0.75), 0.5, True, None, 2), + (3, fractionalOrderFactory('constantNonSym', 0.75), 0.5, False, None, 2), + (3, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, True, None, 2), + (3, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), np.inf, False, None, 2), + (3, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, True, None, 2), + (3, fractionalOrderFactory('twoDomainNonSym', 0.75, 0.25), 0.5, False, None, 2), + (3, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, True, None, 2), + (3, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), np.inf, False, None, 2), + (3, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, True, None, 2), + (3, fractionalOrderFactory('twoDomainNonSym', 0.25, 0.75), 0.5, False, None, 2), + ], ids=idfunc) +def testFractionalKernel(dim, s, horizon, normalized, phi, derivative): if dim == 1: xy_values = [(np.array([-0.1]), np.array([0.1])), (np.array([0.1]), np.array([-0.1])), @@ -553,8 +621,10 @@ def testFractionalKernel(fractionalKernelParams): raise NotImplementedError() kernel = kernelFactory('fractional', dim=dim, s=s, horizon=horizon, normalized=normalized, phi=phi) + assert not kernel.is_integrable + if derivative == 1: - kernel = kernel.getDerivativeKernel() + kernel = kernel.getGradientKernel() elif derivative == 2: kernel = kernel.getHessianKernel() boundaryKernel = kernel.getBoundaryKernel() @@ -566,6 +636,10 @@ def testFractionalKernel(fractionalKernelParams): print(infHorizonKernel) print(boundaryKernelInf) + assert not boundaryKernel.is_integrable + assert not infHorizonKernel.is_integrable + assert not boundaryKernelInf.is_integrable + for x, y in xy_values: sValue = s(x, y) @@ -592,21 +666,21 @@ def testFractionalKernel(fractionalKernelParams): if normalized: if dim == 1: if horizonValue < np.inf: - const = (2.-2*sValue) * pow(horizonValue**2, sValue-1.) * 0.5 + const = (2.-2*sValue) * pow(horizonValue**2, sValue-1.) else: - const = 2.0**(2.0*sValue) * sValue * gamma(sValue+0.5)/sqrt(pi)/gamma(1.0-sValue) * 0.5 + const = 2.0**(2.0*sValue) * sValue * gamma(sValue+0.5)/sqrt(pi)/gamma(1.0-sValue) elif dim == 2: if horizonValue < np.inf: - const = (2.-2*sValue)*pow(horizonValue**2, sValue-1.) * 2./pi * 0.5 + const = (2.-2*sValue)*pow(horizonValue**2, sValue-1.) * 2./pi else: - const = 2.0**(2.0*sValue) * sValue * gamma(sValue+1.0)/pi/gamma(1.-sValue) * 0.5 + const = 2.0**(2.0*sValue) * sValue * gamma(sValue+1.0)/pi/gamma(1.-sValue) elif dim == 3: if horizonValue < np.inf: - const = (2.-2*sValue)*pow(horizonValue**2, sValue-1.) * 3*gamma(dim/2)/pow(pi, dim/2) * 0.5 + const = (2.-2*sValue)*pow(horizonValue**2, sValue-1.) * 3*gamma(dim/2)/pow(pi, dim/2) else: - const = 2.0**(2.0*sValue) * sValue * gamma(sValue+1.5)/pow(pi, 1.5)/gamma(1.-sValue) * 0.5 + const = 2.0**(2.0*sValue) * sValue * gamma(sValue+1.5)/pow(pi, 1.5)/gamma(1.-sValue) else: - const = 0.5 + const = 1.0 if derivative == 0: refInf = const/norm(x-y)**(dim+2*sValue) * phiValue @@ -652,77 +726,92 @@ def testFractionalKernel(fractionalKernelParams): refBoundary = refInfBoundary if (norm(x-y) < horizonValue) else 0. # test the kernel with potentially finite horizon - assert np.isclose(kernel(x, y), ref), (kernel(x, y), ref) + np.testing.assert_allclose(np.sum(kernel(x, y)), ref) # test kernel with infinite horizon - assert np.isclose(infHorizonKernel(x, y), refInf), (infHorizonKernel(x, y), refInf) + np.testing.assert_allclose(np.sum(infHorizonKernel(x, y)), refInf) if phi is not None and not isinstance(phi, constantTwoPoint): # Not implemented, as the boundary kernel will depend on phi continue # test boundary kernel with potentially finite horizon - assert np.isclose(boundaryKernel(x, y), refBoundary*norm(x-y)/sValue), (boundaryKernel(x, y), refBoundary*norm(x-y)/sValue) + n = np.zeros((dim), dtype=REAL) + n[0] = 1. + boundaryKernel.setNormal(n) + np.testing.assert_allclose(np.sum(boundaryKernel(x, y), axis=0), refBoundary*np.dot(x-y, n)/(-2*sValue)) # test boundary kernel with infinite horizon - assert np.isclose(boundaryKernelInf(x, y), refInfBoundary*norm(x-y)/sValue), (boundaryKernelInf(x, y), refInfBoundary*norm(x-y)/sValue) + boundaryKernelInf.setNormal(n) + np.testing.assert_allclose(np.sum(boundaryKernelInf(x, y), axis=0), refInfBoundary*np.dot(x-y, n)/(-2*sValue)) # test that div_y (boundaryKernelInf(x,y) (x-y)/norm(x-y)) == 2*infHorizonKernel(x,y) eps = 1e-8 - div_fd = 0. + div_fd = np.zeros((s.numParameters**derivative)) for i in range(dim): yShifted = y.copy() yShifted[i] += eps - div_fd += (boundaryKernelInf(x, yShifted) * (x-yShifted)[i]/norm(x-yShifted) - boundaryKernelInf(x, y) * (x-y)[i]/norm(x-y))/eps - assert np.isclose(div_fd, 2*infHorizonKernel(x, y), rtol=1e-3), (div_fd, 2*infHorizonKernel(x, y)) - - assert np.isclose(kernel.singularityValue, -dim-2*sValue), (kernel.singularityValue, -dim-2*sValue) - assert np.isclose(boundaryKernel.singularityValue, 1-dim-2*sValue), (boundaryKernel.singularityValue, 1-dim-2*sValue) - assert np.isclose(infHorizonKernel.singularityValue, -dim-2*sValue), (infHorizonKernel.singularityValue, -dim-2*sValue) - assert np.isclose(boundaryKernelInf.singularityValue, 1-dim-2*sValue), (boundaryKernelInf.singularityValue, 1-dim-2*sValue) - + if dim == 1 and (derivative == 0 or s.numParameters == 1): + div_fd[0] += (boundaryKernelInf(x, yShifted) - boundaryKernelInf(x, y))/eps + else: + n[:] = 0. + n[i] = 1. + boundaryKernelInf.setNormal(n) + div_fd += (boundaryKernelInf(x, yShifted) - boundaryKernelInf(x, y))/eps + np.testing.assert_allclose(div_fd, -infHorizonKernel(x, y), rtol=1e-3) -from PyNucleus import dofmapFactory, fractionalOrderFactory, kernelFactory, meshFactory, REAL, functionFactory -from PyNucleus_nl import nonlocalBuilder + np.testing.assert_allclose(kernel.singularityValue, -dim-2*sValue) + np.testing.assert_allclose(boundaryKernel.singularityValue, 1-dim-2*sValue) + np.testing.assert_allclose(infHorizonKernel.singularityValue, -dim-2*sValue) + np.testing.assert_allclose(boundaryKernelInf.singularityValue, 1-dim-2*sValue) -def test_discrete_s_const(): - """ - Compare operators for kernel with - s = constantNonSym(0.75) - and its finite element interpolation. - """ - mesh = meshFactory('interval', a=-1, b=1, hTarget=1e-2) +def constantFractionalKernels(hTarget): + mesh = meshFactory('interval', a=-1, b=1, hTarget=hTarget) dmS = dofmapFactory('P1', mesh, -1) + + sFunSym = fractionalOrderFactory('constant', 0.75) sFun = fractionalOrderFactory('constantNonSym', 0.75) sFE = fractionalOrderFactory('constantNonSym', 0.75, dm=dmS) + + kernelFunSym = kernelFactory('fractional', dim=mesh.dim, s=sFunSym) kernelFun = kernelFactory('fractional', dim=mesh.dim, s=sFun) kernelFE = kernelFactory('fractional', dim=mesh.dim, s=sFE) - dm = dofmapFactory('P1', mesh, 0) - A_fun = dm.assembleNonlocal(kernelFun) - A_fe = dm.assembleNonlocal(kernelFE) - assert np.absolute((A_fun-A_fe).toarray()/A_fun.toarray()).max() == 0. - - -def test_discrete_leftRight(): - """ - Compare operators for kernel with - s = twoDomainNonSym(0.25, 0.75) - and its finite element interpolation. - """ - mesh = meshFactory('interval', a=-1, b=1, hTarget=1e-2) + + return kernelFunSym, kernelFun, kernelFE + + +def linearLeftRightFractionalKernels(hTarget): + mesh = meshFactory('interval', a=-1, b=1, hTarget=hTarget) dmS = dofmapFactory('P1', mesh, -1) - sFun = fractionalOrderFactory('twoDomainNonSym', sl=0.25, sr=0.75, r=0.3) - sFE = fractionalOrderFactory('twoDomainNonSym', sl=0.25, sr=0.75, r=0.3, dm=dmS) + + r = mesh.h + sFun = fractionalOrderFactory('linearLeftRightNonSym', sl=0.25, sr=0.75, r=r) + sFE = fractionalOrderFactory('linearLeftRightNonSym', sl=0.25, sr=0.75, r=r, dm=dmS) + kernelFun = kernelFactory('fractional', dim=mesh.dim, s=sFun) kernelFE = kernelFactory('fractional', dim=mesh.dim, s=sFE) + + assert sFE.vec.max()-sFE.vec.min() == 0.5 + + return kernelFun, kernelFE + + +def assemble(kernel, hTarget, **kwargs): + mesh = meshFactory('interval', a=-1, b=1, hTarget=hTarget) dm = dofmapFactory('P1', mesh, 0) - A_fun = dm.assembleNonlocal(kernelFun) - A_fe = dm.assembleNonlocal(kernelFE) - print('max rel error', np.absolute((A_fun-A_fe).toarray()/A_fun.toarray()).max()) - x = dm.fromArray(np.random.randn(dm.num_dofs)) - a = x.inner(A_fun*x) - b = x.inner(A_fe*x) - print('apply abs error', abs(a-b)) - print('apply rel error', abs(a-b)/a) - assert abs(a-b)/a < 1e-4 + A = dm.assembleNonlocal(kernel, **kwargs) + return A + + +@pytest.mark.parametrize("kernelGenerator", [constantFractionalKernels, linearLeftRightFractionalKernels]) +@pytest.mark.parametrize("matrixFormat", ["dense", "h2"]) +def test_discrete(kernelGenerator, matrixFormat): + hTarget = 1e-2 + kernels = constantFractionalKernels(hTarget) + matrices = [assemble(kernel, hTarget, matrixFormat=matrixFormat) for kernel in kernels] + arrays = [A.toarray() for A in matrices] + for k in range(1, len(arrays)): + np.testing.assert_allclose(arrays[0], arrays[k]) + + diff --git a/tests/test_nearField.py b/tests/test_nearField.py index c62c28bc..5d30c7fa 100644 --- a/tests/test_nearField.py +++ b/tests/test_nearField.py @@ -15,11 +15,11 @@ from PyNucleus_fem.DoFMaps import P0_DoFMap, P1_DoFMap from PyNucleus_fem.functions import constant from PyNucleus_nl import nonlocalBuilder -from PyNucleus_nl.kernels import getFractionalKernel +from PyNucleus_nl.kernels import FractionalKernel from PyNucleus_nl.nonlocalAssembly import nearFieldClusterPair -from PyNucleus_nl.clusterMethodCy import (H2Matrix, - getDoFBoxesAndCells, - tree_node) +from PyNucleus_nl.clusterMethod import (H2Matrix, + getDoFBoxesAndCells, + tree_node) from PyNucleus_nl.fractionalOrders import (constFractionalOrder, variableConstFractionalOrder, leftRightFractionalOrder, @@ -48,7 +48,7 @@ class test: @classmethod def setup_class(self): - kernel = getFractionalKernel(self.dim, self.s, self.horizon, normalized=self.normalized, phi=self.phi, piecewise=self.piecewise) + kernel = FractionalKernel.build(dim=self.dim, s=self.s, horizon=self.horizon, normalized=self.normalized, phi=self.phi, piecewise=self.piecewise) print('\n##################################################') print('Testing: {}'.format(kernel)) @@ -84,7 +84,7 @@ def buildBaseA(self): if not hasattr(self, '_baseA'): if isinstance(self.s, variableConstFractionalOrder) and self.phi is None: s = constFractionalOrder(self.s.value) - kernel = getFractionalKernel(self.dim, s, self.horizon, normalized=True) + kernel = FractionalKernel.build(dim=self.dim, s=s, horizon=self.horizon, normalized=True) self._constBuilder = nonlocalBuilder(self.dm, kernel, params=self.params, zeroExterior=self.zeroExterior) self._baseA = self._constBuilder.getDense() self._baseLabel = 'dense_const' @@ -262,67 +262,67 @@ def compare(self, label, A1, A2): print(A2.diagonal()) print(A1.diagonal()-A2.diagonal()) print() - try: - import matplotlib - import matplotlib.pyplot as plt - except ImportError: - return - if self.mesh.dim == 1: - x = self.dm.getDoFCoordinates() - indMax = np.absolute(A1.diagonal()-A2.diagonal()).argmax() - print(indMax, x[indMax]) - X, Y = np.meshgrid(x, x) - plt.figure() - plt.pcolormesh(X, Y, A1) - plt.colorbar() - plt.title('A1') - - plt.figure() - plt.pcolormesh(X, Y, A2) - plt.colorbar() - plt.title('A2') - - plt.figure() - plt.pcolormesh(X, Y, np.absolute(np.around(A1-A2, 9)), norm=matplotlib.colors.LogNorm()) - plt.colorbar() - plt.title('|A1-A2|') - - plt.figure() - # plt.pcolormesh(X, Y, np.absolute(np.around((A1-A2)/A1, 9)), norm=matplotlib.colors.LogNorm()) - plt.pcolormesh(X, Y, np.log10(np.absolute((A1-A2)/A1))) - plt.colorbar() - plt.title('log |(A1-A2)/A1|') - - plt.show() - else: - plt.figure() - err = self.dm.zeros() - err.assign(np.absolute((A1.diagonal()-A2.diagonal())/(A1.diagonal()))) - err.plot(flat=True) - plt.title('diagonal error') - - plt.figure() - plt.pcolormesh(A1) - plt.colorbar() - plt.title('A1') - - plt.figure() - plt.pcolormesh(A2) - plt.colorbar() - plt.title('A2') - - plt.figure() - plt.pcolormesh(np.absolute(A1-A2), norm=matplotlib.colors.LogNorm()) - plt.colorbar() - plt.title('|A1-A2|') - - plt.figure() - # plt.pcolormesh(np.absolute(np.around((A1-A2)/A1, 9)), norm=matplotlib.colors.LogNorm()) - plt.pcolormesh(np.log10(np.absolute((A1-A2)/A1))) - plt.colorbar() - plt.title('log |(A1-A2)/A1|') - - plt.show() + # try: + # import matplotlib + # import matplotlib.pyplot as plt + # except ImportError: + # return + # if self.mesh.dim == 1: + # x = self.dm.getDoFCoordinates() + # indMax = np.absolute(A1.diagonal()-A2.diagonal()).argmax() + # print(indMax, x[indMax]) + # X, Y = np.meshgrid(x, x) + # plt.figure() + # plt.pcolormesh(X, Y, A1) + # plt.colorbar() + # plt.title('A1') + + # plt.figure() + # plt.pcolormesh(X, Y, A2) + # plt.colorbar() + # plt.title('A2') + + # plt.figure() + # plt.pcolormesh(X, Y, np.absolute(np.around(A1-A2, 9)), norm=matplotlib.colors.LogNorm()) + # plt.colorbar() + # plt.title('|A1-A2|') + + # plt.figure() + # # plt.pcolormesh(X, Y, np.absolute(np.around((A1-A2)/A1, 9)), norm=matplotlib.colors.LogNorm()) + # plt.pcolormesh(X, Y, np.log10(np.absolute((A1-A2)/A1))) + # plt.colorbar() + # plt.title('log |(A1-A2)/A1|') + + # plt.show() + # else: + # plt.figure() + # err = self.dm.zeros() + # err.assign(np.absolute((A1.diagonal()-A2.diagonal())/(A1.diagonal()))) + # err.plot(flat=True) + # plt.title('diagonal error') + + # plt.figure() + # plt.pcolormesh(A1) + # plt.colorbar() + # plt.title('A1') + + # plt.figure() + # plt.pcolormesh(A2) + # plt.colorbar() + # plt.title('A2') + + # plt.figure() + # plt.pcolormesh(np.absolute(A1-A2), norm=matplotlib.colors.LogNorm()) + # plt.colorbar() + # plt.title('|A1-A2|') + + # plt.figure() + # # plt.pcolormesh(np.absolute(np.around((A1-A2)/A1, 9)), norm=matplotlib.colors.LogNorm()) + # plt.pcolormesh(np.log10(np.absolute((A1-A2)/A1))) + # plt.colorbar() + # plt.title('log |(A1-A2)/A1|') + + # plt.show() assert value < epsAbs[(self.dim, self.horizon.value)] and valueRel < epsRel