diff --git a/.github/ISSUE_TEMPLATE/mistake-report.md b/.github/ISSUE_TEMPLATE/mistake-report.md
deleted file mode 100644
index 9fdc9e03e4..0000000000
--- a/.github/ISSUE_TEMPLATE/mistake-report.md
+++ /dev/null
@@ -1,14 +0,0 @@
----
-name: Mistake report
-about: Report a mistake in the database
-title: ''
-labels: bug
-assignees: ''
-
----
-
-**Describe the mistake**
-A clear and concise description of what the bug is, which element(s) it relates to.
-
-**Additional context**
-Add any other context about the mistake here.
diff --git a/.github/ISSUE_TEMPLATE/new-element.md b/.github/ISSUE_TEMPLATE/new-element.md
deleted file mode 100644
index 50dec424b8..0000000000
--- a/.github/ISSUE_TEMPLATE/new-element.md
+++ /dev/null
@@ -1,16 +0,0 @@
----
-name: New element
-about: Suggest a new element for the database
-title: Add [NAME] element
-labels: new element
-assignees: ''
-
----
-
-**Details.**
-Include some information about the element.
-
-**Reference(s)**
-- Links to
-- or titles of references
-- can be put here
diff --git a/.github/ISSUE_TEMPLATE/suggest-an-improvement.md b/.github/ISSUE_TEMPLATE/suggest-an-improvement.md
deleted file mode 100644
index 96cb0a405a..0000000000
--- a/.github/ISSUE_TEMPLATE/suggest-an-improvement.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-name: Suggest an improvement
-about: Suggest an idea for this project
-title: ''
-labels: feature request
-assignees: ''
-
----
-
-Describe your suggestion.
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
deleted file mode 100644
index 6cd87d7879..0000000000
--- a/.github/dependabot.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-# To get started with Dependabot version updates, you'll need to specify which
-# package ecosystems to update and where the package manifests are located.
-# Please see the documentation for all configuration options:
-# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
-
-version: 2
-updates:
- - package-ecosystem: "github-actions"
- directory: "/"
- schedule:
- interval: "weekly"
- cooldown:
- default-days: 7
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
deleted file mode 100644
index 72aa59ab5f..0000000000
--- a/.github/pull_request_template.md
+++ /dev/null
@@ -1,8 +0,0 @@
-Changes in this pull request:
-
-- List
-- your
-- changes
-- here
-
-Fixes # (issue)
diff --git a/.github/scripts/check_dependency_versions.py b/.github/scripts/check_dependency_versions.py
deleted file mode 100644
index 3fbd01efa8..0000000000
--- a/.github/scripts/check_dependency_versions.py
+++ /dev/null
@@ -1,65 +0,0 @@
-"""Check that dependencies are up to date."""
-
-import os
-
-import requests
-
-try:
- import tomllib
-except ModuleNotFoundError: # TODO: remove this once minimum Python version is 3.10
- import tomli as tomllib
-
-green = "\033[32m"
-red = "\033[31m"
-blue = "\033[34m"
-default = "\033[0m"
-
-
-class Version:
- def __init__(self, v: str):
- self.v = v.split(".")
-
- def __eq__(self, other):
- if not isinstance(other, Version):
- return NotImplemented
-
- n = min(len(self.v), len(other.v))
- return self.v[:n] == other.v[:n]
-
- def __str__(self):
- return ".".join(self.v)
-
-
-class DependenciesNeedUpdating(BaseException):
- pass
-
-
-requirements = {}
-with open(
- os.path.join(
- os.path.join(
- os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."),
- "..",
- ),
- "pyproject.toml",
- ),
- "rb",
-) as f:
- for dep in tomllib.load(f)["project"]["dependencies"]:
- lib, version = dep.split("==")
- requirements[lib] = Version(version)
-
-need_updating = {}
-for lib, version in requirements.items():
- libname = lib.split("@")[0].split("[")[0]
- latest = Version(
- requests.get(f"https://pypi.org/pypi/{libname}/json").json()["info"]["version"]
- )
- if version == latest:
- print(f"{green}{lib}{default} is at latest version ({version})")
- else:
- need_updating[lib] = latest
- print(f"{red}{lib}{default} can be updated from {version} to {latest}")
-
-if len(need_updating) > 0:
- raise DependenciesNeedUpdating()
diff --git a/.github/scripts/check_version_number.py b/.github/scripts/check_version_number.py
deleted file mode 100644
index cb8bdd29ab..0000000000
--- a/.github/scripts/check_version_number.py
+++ /dev/null
@@ -1,33 +0,0 @@
-import os
-
-try:
- import tomllib
-except ModuleNotFoundError: # TODO: remove this once minimum Python version is 3.10
- import tomli as tomllib
-
-import github
-
-with open(
- os.path.join(
- os.path.join(
- os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."),
- "..",
- ),
- "pyproject.toml",
- ),
- "rb",
-) as f:
- version = tomllib.load(f)["project"]["version"]
-
-access_key = os.environ.get("GITHUB_TOKEN", None)
-
-git = github.Github(auth=github.Auth.Token(access_key))
-
-defelement = git.get_repo("DefElement/DefElement")
-
-for release in defelement.get_releases():
- if release.tag_name == f"v{version}":
- print("release=no")
- break
-else:
- print(f"release={version}")
diff --git a/.github/scripts/compare_json.py b/.github/scripts/compare_json.py
deleted file mode 100644
index e22cb37159..0000000000
--- a/.github/scripts/compare_json.py
+++ /dev/null
@@ -1,42 +0,0 @@
-"""Check if two JSON files contain the same data."""
-
-import argparse
-import json
-
-parser = argparse.ArgumentParser(description="Check if two JSON files contain the same data")
-parser.add_argument(
- "files",
- metavar="files",
- nargs=2,
- default=None,
- help="Names of input json files.",
-)
-
-args = parser.parse_args()
-
-with open(args.files[0]) as f:
- data0 = json.load(f)
-with open(args.files[1]) as f:
- data1 = json.load(f)
-
-
-def is_equal(a, b):
- if isinstance(a, dict):
- if not isinstance(b, dict) or len(a) != len(b):
- return False
- for ai, aj in a.items():
- if ai not in b or not is_equal(aj, b[ai]):
- return False
- return True
- elif isinstance(a, list):
- if not isinstance(b, list) or len(a) != len(b):
- return False
- for ai, bi in zip(a, b):
- if not is_equal(ai, bi):
- return False
- return True
-
- return a == b
-
-
-assert is_equal(data0, data1)
diff --git a/.github/scripts/make_release.py b/.github/scripts/make_release.py
deleted file mode 100644
index 9e9f909f2a..0000000000
--- a/.github/scripts/make_release.py
+++ /dev/null
@@ -1,30 +0,0 @@
-import os
-from datetime import datetime, timedelta, timezone
-
-import github
-
-access_key = os.environ.get("GITHUB_TOKEN", None)
-version = os.environ.get("VERSION", None)
-tar_gz = f"defelement-v{version}.tar.gz"
-if "PATH" in os.environ:
- tar_gz = os.path.join(os.environ["PATH"], tar_gz)
-
-git = github.Github(auth=github.Auth.Token(access_key))
-
-defelement = git.get_repo("DefElement/DefElement")
-main_branch = defelement.get_branch("main")
-ref = defelement.get_git_ref("heads/main")
-
-release = defelement.create_git_tag_and_release(
- f"v{version}",
- f"v{version}",
- f"v{version}",
- f"Snapshot of DefElement, {datetime.now(tz=timezone(timedelta())).strftime('%d %B %Y')}.\n\nThis release is archived at [doi.org/10.5281/zenodo.17904468](https://doi.org/10.5281/zenodo.17904468)",
- main_branch.commit.sha,
- "commit",
-)
-
-for asset in release.get_assets():
- asset.delete_asset()
-
-release.upload_asset(tar_gz)
diff --git a/.github/scripts/push_verification.py b/.github/scripts/push_verification.py
deleted file mode 100644
index 5d90d184a7..0000000000
--- a/.github/scripts/push_verification.py
+++ /dev/null
@@ -1,35 +0,0 @@
-import os
-import sys
-
-import github
-
-access_key = os.environ.get("GITHUB_TOKEN", None)
-_, path = sys.argv
-
-git = github.Github(auth=github.Auth.Token(access_key))
-
-defelement = git.get_repo("DefElement/DefElement")
-branch = defelement.get_branch("verification")
-
-with open(os.path.join(path, "verification.json")) as f:
- new_verification = f.read()
-with open(os.path.join(path, "verification-history.json")) as f:
- new_verification_history = f.read()
-
-old_verification = branch.get_contents("verification.json")
-defelement.update_file(
- "verification.json",
- "verification.json",
- new_verification,
- old_verification.sha,
- branch="verification",
-)
-
-old_verification_history = branch.get_contents("verification-history.json")
-defelement.update_file(
- "verification-history.json",
- "verification-history.json",
- new_verification_history,
- old_verification_history.sha,
- branch="verification",
-)
diff --git a/.github/scripts/update_version_number.py b/.github/scripts/update_version_number.py
deleted file mode 100644
index 68897b16f9..0000000000
--- a/.github/scripts/update_version_number.py
+++ /dev/null
@@ -1,58 +0,0 @@
-import os
-from datetime import datetime, timedelta, timezone
-
-import github
-
-version = datetime.now(tz=timezone(timedelta())).strftime("%Y.%m")
-branch_name = f"v{version}"
-
-access_key = os.environ.get("PA_TOKEN", None)
-
-git = github.Github(auth=github.Auth.Token(access_key))
-
-defelement = git.get_repo("DefElement/DefElement")
-main_branch = defelement.get_branch("main")
-ref = defelement.get_git_ref("heads/main")
-base_tree = defelement.get_git_tree(main_branch.commit.sha)
-
-defelement.create_git_ref(ref=f"refs/heads/{branch_name}", sha=main_branch.commit.sha)
-new_branch = defelement.get_branch(branch_name)
-
-# Update pyproject.toml
-pyproject_file = defelement.get_contents("pyproject.toml", main_branch.commit.sha)
-pyproject = pyproject_file.decoded_content.decode("utf8")
-pre_project, post_project = pyproject.split("[project]\n")
-pre_version, post_version = post_project.split("version = ")
-post_version = post_version.split("\n", 1)[1]
-pyproject = f'{pre_project}[project]\n{pre_version}version = "{version}"\n{post_version}'
-defelement.update_file(
- "pyproject.toml",
- "[AUTOMATED] Update version number",
- pyproject,
- sha=pyproject_file.sha,
- branch=branch_name,
-)
-
-# Update .zenodo.json
-zenodo_file = defelement.get_contents(".zenodo.json", main_branch.commit.sha)
-zenodo = zenodo_file.decoded_content.decode("utf8")
-pre_version, post_version = zenodo.split('"version": "')
-post_version = post_version.split('"', 1)[1]
-zenodo = f'{pre_version}"version": "v{version}"{post_version}'
-pre_pubdate, post_pubdate = zenodo.split('"publication_date": "')
-post_pubdate = post_pubdate.split('"', 1)[1]
-zenodo = f'{pre_pubdate}"publication_date": "{datetime.now(tz=timezone(timedelta())).strftime("%Y-%m-%d")}"{post_pubdate}'
-defelement.update_file(
- ".zenodo.json",
- "[AUTOMATED] Update version number",
- zenodo,
- sha=zenodo_file.sha,
- branch=branch_name,
-)
-
-pr = defelement.create_pull(
- title="[AUTOMATED] Update version number", body="", base="main", head=branch_name
-)
-pr.enable_automerge("SQUASH")
-
-print(f"branch={branch_name}")
diff --git a/.github/scripts/upload_to_zenodo.py b/.github/scripts/upload_to_zenodo.py
deleted file mode 100644
index c2c37ded9e..0000000000
--- a/.github/scripts/upload_to_zenodo.py
+++ /dev/null
@@ -1,83 +0,0 @@
-import json
-import os
-
-import requests
-
-token = os.environ.get("ZENODO_TOKEN", None)
-version = os.environ.get("VERSION", None)
-tar_gz = f"defelement-v{version}.tar.gz"
-if "PATH" in os.environ:
- tar_gz = os.path.join(os.environ["PATH"], tar_gz)
-
-doi = "10.5281/zenodo.17904468"
-api_url = "zenodo.org"
-# api_url = "sandbox.zenodo.org"
-
-headers = {"Authorization": f"Bearer {token}"}
-filename = tar_gz.split("/")[-1]
-root_dir = os.path.join(
- os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."),
- "..",
-)
-
-with open(os.path.join(root_dir, ".zenodo.json")) as f:
- metadata = json.load(f)
-
-
-def post(url, **params):
- print(f"POST {url}")
- r = requests.post(url, params=params, headers=headers).json()
- print(r)
- return r
-
-
-def get(url, **params):
- print(f"GET {url}")
- r = requests.get(url, params=params, headers=headers).json()
- print(r)
- return r
-
-
-def put(url, data=None, content_type=None, **params):
- print(f"PUT {url}")
- if content_type is None:
- put_headers = headers
- else:
- put_headers = {**headers, "Content-Type": content_type}
- r = requests.put(url, data=data, params=params, headers=put_headers).json()
- print(r)
- return r
-
-
-def delete(url, **params):
- print(f"DELETE {url}")
- r = requests.delete(url, params=params, headers=headers)
- print(r)
- return r
-
-
-r = get(f"https://{api_url}/api/records", q=f"conceptdoi:{doi}")
-id = r["hits"]["hits"][0]["id"]
-
-r = get(f"https://{api_url}/api/deposit/depositions/{id}")
-newversion_link = r["links"]["newversion"]
-
-r = post(newversion_link)
-new_id = r["links"]["latest_draft"].split("/")[-1]
-bucket_link = r["links"]["bucket"]
-
-for f in r["files"]:
- delete(f["links"]["self"])
-
-r = put(
- f"https://{api_url}/api/deposit/depositions/{new_id}",
- json.dumps(metadata),
- "application/json",
-)
-
-with open(tar_gz, "rb") as f:
- r = put(f"{bucket_link}/{filename}", f)
-
-r = post(f"https://{api_url}/api/deposit/depositions/{new_id}/actions/publish")
-
-assert r["state"] == "done"
diff --git a/.github/set_symfem_branch.py b/.github/set_symfem_branch.py
deleted file mode 100644
index 41a6405093..0000000000
--- a/.github/set_symfem_branch.py
+++ /dev/null
@@ -1,31 +0,0 @@
-"""Script to update Symfem branch in CI.
-
-For example, to make the tests run using a Symfem branch called "test2", run:
- python3 set_symfem_branch.py test2
-"""
-
-import argparse
-import os
-
-parser = argparse.ArgumentParser(description="Set Symfem branch")
-parser.add_argument("branch")
-branch = parser.parse_args().branch
-
-print(f'Setting Symfem branch to "{branch}"')
-
-for file in os.listdir("workflows"):
- skip = False
- content = ""
- with open(os.path.join("workflows", file)) as f:
- for line in f:
- if skip:
- assert "ref:" in line
- skip = False
- else:
- content += line
- if "repository: mscroggs/symfem" in line:
- content += line.split("repository")[0]
- content += f"ref: {branch}\n"
- skip = True
- with open(os.path.join("workflows", file), "w") as f:
- f.write(content)
diff --git a/.github/workflows/archive.yml b/.github/workflows/archive.yml
deleted file mode 100644
index 5cec352973..0000000000
--- a/.github/workflows/archive.yml
+++ /dev/null
@@ -1,133 +0,0 @@
-name: Archive DefElement
-
-on:
- push:
- branches: [ main ]
- workflow_dispatch:
-
-permissions: {}
-
-concurrency:
- group: archive-${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- check-version-number:
- name: Check version number
- runs-on: ubuntu-22.04
- outputs:
- release: ${{ steps.version.outputs.release }}
- steps:
- - name: Set up Python
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
- with:
- python-version: "3.14"
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- persist-credentials: false
-
- - name: Install PyGitHub
- run: python3 -m pip install PyGitHub
-
- - name: Check version number
- run: python3 .github/scripts/check_version_number.py >> $GITHUB_OUTPUT
- id: version
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- permissions:
- contents: read
-
- build-archive:
- name: Build archive
- runs-on: ubuntu-22.04
- needs:
- - check-version-number
- permissions:
- id-token: write # Needed by push
- contents: write # Allow pushing of zip
- if: needs.check-version-number.outputs.release != 'no'
- steps:
- - name: Set up Python
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
- with:
- python-version: "3.14"
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- persist-credentials: false
- - name: Clone verification
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- path: ./verification
- repository: DefElement/DefElement
- ref: verification
- persist-credentials: false
- continue-on-error: true
-
- - name: Move verification jsons
- run: |
- mv verification/*.json .
- rm -r verification
-
- - run: mkdir -p ~/.local/share/fonts
- name: Make font folder
- - name: Clone Varela Round
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- path: ./varela-r
- repository: avrahamcornfeld/Varela-Round-Hebrew
- ref: master
- persist-credentials: false
- - run: |
- cp varela-r/fonts/VarelaRound-Regular.otf ~/.local/share/fonts/
- rm -r varela-r
- name: Install Varela Round
- - name: Download and install Computer Modern
- run: |
- wget https://downloads.sourceforge.net/project/cm-unicode/cm-unicode/0.7.0/cm-unicode-0.7.0-otf.tar.xz
- tar -xf cm-unicode-0.7.0-otf.tar.xz
- cp cm-unicode-0.7.0/cmunti.otf ~/.local/share/fonts/
- rm -r cm-unicode-0.7.0
- rm cm-unicode-0.7.0-otf.tar.xz
-
- - run: python3 -m pip install .
- name: Install defelement
- - run: python3 build.py _html --verification-json verification.json --processes 4
- name: Build website HTML
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Install implementations
- run: |
- python3 -m pip install setuptools
- python3 install_implementations.py
-
- - name: Remove build cache
- run: rm -r .defelement-build-cache
- - name: Remove __pycache__
- run: |
- rm -r defelement/__pycache__
- rm -r defelement/implementations/__pycache__
-
- - name: Make lock file
- run: python3 -m pip freeze > requirements.lock
-
- - name: Make tar.gz
- run: tar --exclude '.git' -czvf ../defelement-v${NEEDS_CHECK_VERSION_NUMBER_OUTPUTS_RELEASE}.tar.gz .
- env:
- NEEDS_CHECK_VERSION_NUMBER_OUTPUTS_RELEASE: ${{ needs.check-version-number.outputs.release }}
-
- - name: Install PyGitHub
- run: python3 -m pip install PyGitHub
- - name: Make release
- run: python3 .github/scripts/make_release.py
- env:
- PATH: ".."
- VERSION: ${{ needs.check-version-number.outputs.release }}
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Upload to Zenodo
- run: python3 .github/scripts/upload_to_zenodo.py
- env:
- PATH: ".."
- VERSION: ${{ needs.check-version-number.outputs.release }}
- ZENODO_TOKEN: ${{ secrets.ZENODO_TOKEN }}
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
deleted file mode 100644
index 2f0ca48a6b..0000000000
--- a/.github/workflows/build.yml
+++ /dev/null
@@ -1,128 +0,0 @@
-name: Build and deploy DefElement
-
-on:
- workflow_call:
- workflow_dispatch:
-
-permissions: {}
-
-concurrency:
- group: build-${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- build-website:
- name: Build
- runs-on: ubuntu-22.04
- steps:
- - name: Set up Python
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
- with:
- python-version: "3.14"
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- persist-credentials: false
-
- - name: Load Symfem cache
- uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
- with:
- path: /home/runner/.cache/symfem
- key: symfem-cache
- - name: Load DefElement cache
- uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
- with:
- path: .defelement-build-cache
- key: defelement-cache
-
- - name: Clone Symfem
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- path: ./symfem
- repository: mscroggs/symfem
- ref: main
- persist-credentials: false
- - name: Install Symfem
- run: |
- cd symfem
- python3 -m pip install .[optional]
-
- - name: Clone verification
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- path: ./verification
- repository: DefElement/DefElement
- ref: verification
- persist-credentials: false
- continue-on-error: true
-
- - run: mkdir -p ~/.local/share/fonts
- name: Make font folder
- - name: Clone Varela Round
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- path: ./varela-r
- repository: avrahamcornfeld/Varela-Round-Hebrew
- ref: master
- persist-credentials: false
- - run: cp varela-r/fonts/VarelaRound-Regular.otf ~/.local/share/fonts/
- name: Install Varela Round
- - name: Download and install Computer Modern
- run: |
- wget https://downloads.sourceforge.net/project/cm-unicode/cm-unicode/0.7.0/cm-unicode-0.7.0-otf.tar.xz
- tar -xf cm-unicode-0.7.0-otf.tar.xz
- cp cm-unicode-0.7.0/cmunti.otf ~/.local/share/fonts/
-
- - run: python3 -m pip install .
- name: Install defelement
- - run: python3 build.py _html --verification-json verification/verification.json --processes 4
- name: Build website HTML
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- if: ${{ !github.event.pull_request.head.repo.fork }}
- - run: python3 build.py _html --verification-json verification/verification.json --processes 4
- name: Build website HTML
- if: ${{ github.event.pull_request.head.repo.fork }}
-
- - name: Setup Pages
- uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
- if: github.ref == 'refs/heads/main'
- - name: Upload artifact
- uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
- with:
- path: '_html'
- if: github.ref == 'refs/heads/main'
-
- - name: Save Symfem cache
- uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
- with:
- path: /home/runner/.cache/symfem
- key: symfem-cache-${{ github.run_id }}-${{ github.run_attempt }}
- if: github.ref == 'refs/heads/main'
- - name: Tidy DefElement cache
- run: python -c "from defelement.caching import tidy_cache; tidy_cache()"
- if: github.ref == 'refs/heads/main'
- - name: Save DefElement cache
- uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
- with:
- path: .defelement-build-cache
- key: defelement-cache-${{ github.run_id }}-${{ github.run_attempt }}
- if: github.ref == 'refs/heads/main'
- permissions:
- contents: read
-
- deploy-website:
- name: Deploy
- runs-on: ubuntu-22.04
- if: github.ref == 'refs/heads/main'
- needs:
- - build-website
- permissions:
- contents: read
- pages: write # Allow updating of Github Pages website
- id-token: write # Needed by Pages
- environment:
- name: github-pages
- url: ${{ steps.deployment.outputs.page_url }}
- steps:
- - name: Deploy to GitHub Pages
- uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
diff --git a/.github/workflows/check-dependency-versions.yml b/.github/workflows/check-dependency-versions.yml
deleted file mode 100644
index ecdb450fea..0000000000
--- a/.github/workflows/check-dependency-versions.yml
+++ /dev/null
@@ -1,33 +0,0 @@
-name: Check dependencies are up to date
-
-on:
- schedule:
- - cron: "0 7 * * 1"
- workflow_dispatch:
-
-permissions: {}
-
-concurrency:
- group: check-dependency-versions-${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- check_dependency_versions:
- name: Check dependencies are up to date
- runs-on: ubuntu-22.04
- steps:
- - name: Set up Python
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
- with:
- python-version: "3.14"
-
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- persist-credentials: false
-
- - name: Check versions
- run: |
- python3 -m pip install requests
- python3 .github/scripts/check_dependency_versions.py
- permissions:
- contents: read
diff --git a/.github/workflows/test-and-build.yml b/.github/workflows/test-and-build.yml
deleted file mode 100644
index d0e1a1a244..0000000000
--- a/.github/workflows/test-and-build.yml
+++ /dev/null
@@ -1,211 +0,0 @@
-name: Test and build
-
-on:
- push:
- branches: [ main ]
- pull_request:
- branches: [ main ]
- schedule:
- - cron: "0 7 * * 1"
-
-permissions: {}
-
-concurrency:
- group: test-and-build-${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- style-checks:
- name: Run style checks
- runs-on: ubuntu-22.04
- steps:
- - name: Set up Python
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
- with:
- python-version: "3.14"
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- persist-credentials: false
-
- - run: |
- python3 -m pip install .[style]
- name: Install defelement
- - run: |
- python3 -m ruff format --check .
- python3 -m ruff check .
- name: Ruff checks
- - run: |
- python3 -m pydocstyle .
- name: Pydocstyle checks
- - run: |
- python3 -m mypy --install-types --non-interactive .
- python3 -m mypy .
- name: mypy checks
-
- test-build-website:
- name: Test building of defelement
- runs-on: ubuntu-22.04
- steps:
- - name: Set up Python
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
- with:
- python-version: "3.14"
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- persist-credentials: false
-
- - name: Clone Symfem
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- path: ./symfem
- repository: mscroggs/symfem
- ref: main
- persist-credentials: false
- - name: Install Symfem
- run: |
- cd symfem
- python3 -m pip install .[optional]
-
- - name: Clone verification
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- path: ./verification
- repository: DefElement/DefElement
- ref: verification
- persist-credentials: false
- continue-on-error: true
-
- - run: mkdir -p ~/.local/share/fonts
- name: Make font folder
- - name: Clone Varela Round
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- path: ./varela-r
- repository: avrahamcornfeld/Varela-Round-Hebrew
- ref: master
- persist-credentials: false
- - run: cp varela-r/fonts/VarelaRound-Regular.otf ~/.local/share/fonts/
- name: Install Varela Round
- - name: Download and install Computer Modern
- run: |
- wget https://downloads.sourceforge.net/project/cm-unicode/cm-unicode/0.7.0/cm-unicode-0.7.0-otf.tar.xz
- tar -xf cm-unicode-0.7.0-otf.tar.xz
- cp cm-unicode-0.7.0/cmunti.otf ~/.local/share/fonts/
-
- - run: python3 -m pip install .
- name: Install defelement
- - run: python3 build.py _test_html --no-cache --verification-json verification/verification.json --test auto --processes 4 --include-simplefem
- name: Build website HTML
- permissions:
- contents: read
-
- build-website:
- name: Build and deploy website
- needs:
- - test-build-website
- - run-tests
- uses: ./.github/workflows/build.yml
- permissions:
- contents: read
- pages: write # Allow updating of website
- id-token: write # Needed by pages
-
- verification:
- name: Test verification
- runs-on: ubuntu-22.04
- steps:
- - name: Set up Python
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
- with:
- python-version: "3.14"
-
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- persist-credentials: false
-
- - name: Load Symfem cache
- uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
- with:
- path: /home/runner/.cache/symfem
- key: symfem-cache
-
- - name: Clone Symfem
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- path: ./symfem
- repository: mscroggs/symfem
- ref: main
- persist-credentials: false
- - name: Install Symfem
- run: |
- cd symfem
- python3 -m pip install .[optional]
-
- - run: python3 -m pip install .
- name: Install defelement
-
- - name: Install implementations
- run: |
- python3 -m pip install setuptools
- python3 install_implementations.py --install-type verification
-
- - run: python3 verify.py --fail-on-missing-libraries --impl simplefem --assert-passing --print-reasons
- name: Check that simplefem verification passes
-
- - run: python3 verify.py verification-serial.json --test auto --fail-on-missing-libraries
- name: Run verification test in serial
- - run: python3 verify.py verification-4.json --test auto --processes 4 --fail-on-missing-libraries
- name: Run verification test on 4 processes
- - name: Check that two verifications runs give the same results
- run: python3 .github/scripts/compare_json.py verification-serial.json verification-4.json
- permissions:
- contents: read
-
- run-tests:
- name: Run tests
- runs-on: ubuntu-22.04
- steps:
- - name: Set up Python
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
- with:
- python-version: "3.14"
-
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- persist-credentials: false
-
- - run: python3 -m pip install pytest-xdist
- name: Install dependencies
-
- - name: Load Symfem cache
- uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
- with:
- path: /home/runner/.cache/symfem
- key: symfem-cache
-
- - name: Clone Symfem
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- path: ./symfem
- repository: mscroggs/symfem
- ref: main
- persist-credentials: false
- - name: Install Symfem
- run: |
- cd symfem
- python3 -m pip install .[optional]
-
- - run: python3 -m pip install .
- name: Install defelement
- - name: Install implementations
- run: python3 install_implementations.py --install-type all
-
- - name: Install LaTeΧ
- run: |
- sudo apt-get update
- sudo apt-get install -y texlive-latex-base
-
- - run: python3 -m pytest -n4 test
- name: Run tests with pytest
- permissions:
- contents: read
diff --git a/.github/workflows/update-version-number.yml b/.github/workflows/update-version-number.yml
deleted file mode 100644
index 420729f566..0000000000
--- a/.github/workflows/update-version-number.yml
+++ /dev/null
@@ -1,37 +0,0 @@
-name: Update version number
-
-on:
- schedule:
- - cron: "0 7 2 */3 *"
- workflow_dispatch:
-
-permissions: {}
-
-concurrency:
- group: update-version-number-${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- update-version-number:
- name: Update version number
- runs-on: ubuntu-22.04
- permissions:
- id-token: write # Needed for puching
- contents: write # Allow writing of new tag
- pull-requests: write # Allow opening PR to update version numbers
- steps:
- - name: Set up Python
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
- with:
- python-version: "3.14"
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- persist-credentials: false
-
- - name: Install PyGitHub
- run: python3 -m pip install PyGitHub
-
- - name: Set version number
- run: python3 .github/scripts/update_version_number.py
- env:
- PA_TOKEN: ${{ secrets.PA_TOKEN }}
diff --git a/.github/workflows/verification.yml b/.github/workflows/verification.yml
deleted file mode 100644
index ba2de2548c..0000000000
--- a/.github/workflows/verification.yml
+++ /dev/null
@@ -1,85 +0,0 @@
-name: Verification
-
-on:
- schedule:
- - cron: "0 7 1 * *"
- workflow_dispatch:
-
-permissions: {}
-
-concurrency:
- group: verification-${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- verification:
- permissions:
- id-token: write # Needed to make push work
- contents: write # To upload verification data
- name: Run verification
- runs-on: ubuntu-22.04
- steps:
- - name: Set up Python
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
- with:
- python-version: "3.14"
-
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- persist-credentials: false
-
- - name: Load Symfem cache
- id: cache-restore
- uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
- with:
- path: /home/runner/.cache/symfem
- key: symfem-cache
-
- - name: Clone Symfem
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- path: ./symfem
- repository: mscroggs/symfem
- ref: main
- persist-credentials: false
- - name: Install Symfem
- run: |
- cd symfem
- python3 -m pip install .[optional]
-
- - run: python3 -m pip install .
- name: Install defelement
-
- - name: Install implementations
- run: |
- python3 -m pip install setuptools
- python3 install_implementations.py --install-type verification
-
- - name: Clone verification history
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- path: ./verification-old
- repository: DefElement/DefElement
- ref: verification
- persist-credentials: false
-
- - run: |
- mkdir ../verification
- cp verification-old/verification-history.json ../verification
- name: Make verification dir containing history
- - run: python3 verify.py ../verification/verification.json --processes 4
- name: Run verification
- - run: python3 .github/scripts/push_verification.py ../verification
- name: Push to GitHub
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- build-and-deply-website:
- name: Build and deploy website
- needs:
- - verification
- uses: ./.github/workflows/build.yml
- permissions:
- contents: read
- pages: write # Allow updating of Github Pages website
- id-token: write # Needed by pages
diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml
deleted file mode 100644
index b4f06b1fca..0000000000
--- a/.github/workflows/zizmor.yml
+++ /dev/null
@@ -1,34 +0,0 @@
-name: Zizmor checks
-
-on:
- push:
- branches: [ main ]
- pull_request:
- branches: [ main ]
-
-permissions: {}
-
-concurrency:
- group: zizmor-${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- zizmor:
- name: Run Zizmor checks
- runs-on: ubuntu-22.04
- steps:
- - name: Set up Python
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
- with:
- python-version: "3.14"
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- persist-credentials: false
-
- - name: Install zizmor
- run: python3 -m pip install zizmor
-
- - name: Run Zizmor checks
- run: zizmor --pedantic .
- permissions:
- contents: read
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index c36e63f335..0000000000
--- a/.gitignore
+++ /dev/null
@@ -1,10 +0,0 @@
-*.pyc
-_html
-verification.json
-verification-history.json
-.defelement-build-cache
-
-*.xcf
-_temp*
-*.log
-
diff --git a/.zenodo.json b/.zenodo.json
deleted file mode 100644
index 585b1d129e..0000000000
--- a/.zenodo.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
- "metadata": {
- "upload_type": "software",
- "license": "mit",
- "access_right": "open",
- "creators": [
- {
- "name": "Scroggs, Matthew W.",
- "affiliation": "University College London",
- "orcid": "0000-0002-4658-2443"
- },
- {
- "name": "Brubeck, Pablo D.",
- "affiliation": "Oxford University",
- "orcid": "0000-0002-3824-0080"
- },
- {
- "name": "Dean, Joseph P.",
- "affiliation": "University of Cambridge",
- "orcid": "0000-0001-7499-3373"
- },
- {
- "name": "Dokken, J\u00f8rgen S.",
- "affiliation": "Simula Research Laboratory",
- "orcid": "0000-0001-6489-8858"
- },
- {
- "name": "Marsden, India",
- "affiliation": "Oxford University",
- "orcid": "0009-0006-0152-4780"
- }
- ],
- "title": "DefElement: an encyclopedia of finite element definitions",
- "version": "v2026.07",
- "publication_date": "2026-07-02",
- "keywords": ["finite element method"]
- }
-}
diff --git a/CITATION.cff b/CITATION.cff
deleted file mode 100644
index cd1ff92920..0000000000
--- a/CITATION.cff
+++ /dev/null
@@ -1,43 +0,0 @@
-cff-version: 1.2.0
-message: If you use this software, please cite it as below.
-authors:
- - family-names: Scroggs
- given-names: Matthew W.
- orcid: 0000-0002-4658-2443
- - family-names: Brubeck
- given-names: Pablo D.
- orcid: https://orcid.org/0000-0002-3824-0080
- - family-names: Dean
- given-names: Joseph P.
- orcid: https://orcid.org/0000-0001-7499-3373
- - family-names: Dokken
- given-names: Jørgen S.
- orcid: https://orcid.org/0000-0001-6489-8858
- - family-names: Marsden
- given-names: India
- orcid: https://orcid.org/0009-0006-0152-4780
-title: "DefElement: an encyclopedia of finite element definitions"
-license: MIT
-url: https://github.com/DefElement/DefElement
-preferred-citation:
- type: article
- authors:
- - family-names: Scroggs
- given-names: Matthew W.
- orcid: https://orcid.org/0000-0002-4658-2443
- - family-names: Brubeck
- given-names: Pablo D.
- orcid: https://orcid.org/0000-0002-3824-0080
- - family-names: Dean
- given-names: Joseph P.
- orcid: https://orcid.org/0000-0001-7499-3373
- - family-names: Dokken
- given-names: Jørgen S.
- orcid: https://orcid.org/0000-0001-6489-8858
- - family-names: Marsden
- given-names: India
- orcid: https://orcid.org/0009-0006-0152-4780
- doi: 10.48550/arXiv.2506.20188
- journal: submitted to Computational Science and Engineering
- title: "DefElement: an encyclopedia of finite element definitions"
- year: 2025
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
deleted file mode 100644
index dd3615e1a5..0000000000
--- a/CODE_OF_CONDUCT.md
+++ /dev/null
@@ -1,126 +0,0 @@
-# Code of conduct
-
-## Our pledge
-
-We as members, contributors, and leaders pledge to make participation in our
-community a harassment-free experience for everyone, regardless of age, body
-size, visible or invisible disability, ethnicity, sex characteristics, gender
-identity and expression, level of experience, education, socio-economic status,
-nationality, personal appearance, race, religion, or sexual identity
-and orientation.
-
-We pledge to act and interact in ways that contribute to an open, welcoming,
-diverse, inclusive, and healthy community.
-
-## Our standards
-
-Examples of behavior that contributes to a positive environment for our
-community include:
-
-* Demonstrating empathy and kindness toward other people
-* Being respectful of differing opinions, viewpoints, and experiences
-* Giving and gracefully accepting constructive feedback
-* Accepting responsibility and apologizing to those affected by our mistakes,
- and learning from the experience
-* Focusing on what is best not just for us as individuals, but for the
- overall community
-
-Examples of unacceptable behavior include:
-
-* The use of sexualized language or imagery, and sexual attention or
- advances of any kind
-* Trolling, insulting or derogatory comments, and personal or political attacks
-* Public or private harassment
-* Publishing others' private information, such as a physical or email
- address, without their explicit permission
-* Other conduct which could reasonably be considered inappropriate in a
- professional setting
-
-## Enforcement responsibilities
-
-Community leaders are responsible for clarifying and enforcing our standards of
-acceptable behavior and will take appropriate and fair corrective action in
-response to any behavior that they deem inappropriate, threatening, offensive,
-or harmful.
-
-Community leaders have the right and responsibility to remove, edit, or reject
-comments, commits, code, wiki edits, issues, and other contributions that are
-not aligned to this code of conduct, and will communicate reasons for moderation
-decisions when appropriate.
-
-## Scope
-
-This code of conduct applies within all community spaces, and also applies when
-an individual is officially representing the community in public spaces.
-Examples of representing our community include using an official e-mail address,
-posting via an official social media account, or acting as an appointed
-representative at an online or offline event.
-
-## Enforcement
-
-Instances of abusive, harassing, or otherwise unacceptable behavior may be
-reported to the community leaders responsible for enforcement at
-defelement@mscroggs.co.uk (Matthew Scroggs). All complaints will be reviewed and
-investigated promptly and fairly.
-
-All community leaders are obligated to respect the privacy and security of the
-reporter of any incident.
-
-## Enforcement guidelines
-
-Community leaders will follow these community impage guidelines in determining
-the consequences for any action they deem in violation of this code of conduct:
-
-### 1. Correction
-
-**Community Impact**: Use of inappropriate language or other behavior deemed
-unprofessional or unwelcome in the community.
-
-**Consequence**: A private, written warning from community leaders, providing
-clarity around the nature of the violation and an explanation of why the
-behavior was inappropriate. A public apology may be requested.
-
-### 2. Warning
-
-**Community Impact**: A violation through a single incident or series
-of actions.
-
-**Consequence**: A warning with consequences for continued behavior. No
-interaction with the people involved, including unsolicited interaction with
-those enforcing the code of conduct, for a specified period of time. This
-includes avoiding interactions in community spaces as well as external channels
-like social media. Violating these terms may lead to a temporary or
-permanent ban.
-
-### 3. Temporary ban
-
-**Community Impact**: A serious violation of community standards, including
-sustained inappropriate behavior.
-
-**Consequence**: A temporary ban from any sort of interaction or public
-communication with the community for a specified period of time. No public or
-private interaction with the people involved, including unsolicited interaction
-with those enforcing the code of conduct, is allowed during this period.
-Violating these terms may lead to a permanent ban.
-
-### 4. Permanent ban
-
-**Community Impact**: Demonstrating a pattern of violation of community
-standards, including sustained inappropriate behavior, harassment of an
-individual, or aggression toward or disparagement of classes of individuals.
-
-**Consequence**: A permanent ban from any sort of public interaction within
-the community.
-
-## Attribution
-
-This code of conduct is adapted from the [Contributor covenant](https://www.contributor-covenant.org),
-version 2.0, available at
-[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html](https://www.contributor-covenant.org/version/2/0/code_of_conduct.html).
-
-Community impact guidelines were inspired by
-[Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
-
-For answers to common questions about this code of conduct, see the FAQ at
-[https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq). Translations are available
-at [https://www.contributor-covenant.org/translations](https://www.contributor-covenant.org/translations).
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index 5d745cb20f..0000000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,178 +0,0 @@
-# Contributing to DefElement
-
-## Making suggestions
-
-### Reporting mistakes
-If you find a mistake in the DefElement database, please report it on the
-[issue tracker](https://github.com/DefElement/DefElement/issues/new?assignees=&labels=bug&template=mistake-report.md&title=)
-using the *Mistake report* template.
-
-### Suggesting new elements
-If you want to suggest a new element to be added to DefElement, suggest it on the
-[issue tracker](https://github.com/DefElement/DefElement/issues/new?assignees=&labels=new+element&template=new-element.md&title=Add+%5BNAME%5D+element)
-using the *New element* template.
-
-### Suggesting improvements
-If you want to suggest a new feature or improvement to DefElement, suggest it on the
-[issue tracker](https://github.com/DefElement/DefElement/issues/new?assignees=&labels=feature+request&template=suggest-an-improvement.md&title=)
-using the *Suggest an improvement* template.
-
-### Discussion ideas for new features
-You can use [Github's Discussions](https://github.com/DefElement/DefElement/discussions) to discuss
-ideas you have for features (that perhaps aren't fully formed enough yet to make an issue) or to
-discuss other people's ideas. You're welcome to also use the Discussions to just chat to other
-members of the community.
-
-## Contributing directly
-
-### Submitting a pull request
-If you want to directly submit changes to DefElement, you can do this by forking the [DefElement Github repository](https://github.com/DefElement/DefElement),
-making changes, then submitting a pull request.
-If you want to contribute, but are unsure where to start, have a look at the
-[issue tracker](https://github.com/DefElement/DefElement/labels/good%20first%20issue) for issues labelled "good first issue".
-
-The functional information and examples on the element pages are generated using
-[Symfem](https://github.com/mscroggs/symfem), a symbolic finite element definition library.
-Before adding an element to DefElement, it should first be implemented in Symfem.
-
-### Defining an element
-Elements in the DefElement database are defined using a yaml file in the `elements/` folder.
-The entries in this yaml file are:
-
-
-
-
Name
Required
Description
-
-
`name`
{{tick}}
The name of the element (ascii).
-
`html‑name`
{{tick}}
The name of the element, including HTML special characters.
-
`reference‑cells`
{{tick}}
The reference cell(s) that this finite element can be defined on.
-
`alt‑names`
Alternative (HTML) names of the element.
-
`legacy‑names`
Filenames that have previously been used for this element and/or filenames of elements that were merged into this element. Pages that redirect from the old names to new name will be created if this is set.
-
`short‑names`
Abbreviated names of the element.
-
`variants`
Variants of this element.
-
`complexes`
Any discretiations of complexes that this element is part of.
-
`dofs`
Description of the DOFs of this element.
-
`ndofs`
The number of DOFs the element has and the A-numbers of the [OEIS](http://oeis.org) sequence(s) giving the number of DOFs.
-
`entity‑ndofs`
The number of DOFs the element has per sub-entity type and the A-numbers of the [OEIS](http://oeis.org) sequence(s) giving the number of DOFs.
-
`polynomial‑set`
The polynomial set of this element. This can use sets defined in the file [`/data/polysets`](https://github.com/DefElement/DefElement/blob/main/data/polysets). Other sets can be given by writing `[LaTeX definition of set]`. Unions of multiple sets can be given, separated by ` && `.
-
`mixed`
If this element is a mixed element, the subelements that it contains.
-
`mapping`
The mapping used to push/pull values foward/back from/to the reference cell.
-
`sobolev`
The Sobolev space the element lives in.
-
`min‑degree`
The minimum degree of the element
-
`max‑degree`
The maximum degree of the element
-
`polynomial-subdegree`
The degree of the highest degree complete polynomial space that is a subspace of this element's polynomial space
-
`polynomial-superdegree`
The degree of the lowest degree complete polynomial space that is a superspace of this element's polynomial space
-
`lagrange-subdegree`
The degree of the highest degree Lagrange space that is a subspace of this element's polynomial space
-
`lagrange-superdegree`
The degree of the lowest degree Lagrange space that is a superspace of this element's polynomial space
-
`degree`
Degree that is use to index this element, should be `polynomial-subdegree`, `polynomial-superdegree`, `lagrange-subdegree`, `lagrange-superdegree`
-
`examples`
Reference cells and degrees to be included in the examples section of the entry.
-
`notes`
Notes about the element.
-
`references`
References to where the element is defined.
-
`categories`
Categories the element belongs to. Categories are defined in the file [`/data/categories`](https://github.com/DefElement/DefElement/blob/main/data/categories).
-
`implementations`
Strings/enum entries/etc to create this element in supported implementations.
-
-
-#### Implementations
-In the `implementations` fields, parameters can be passed to the implementation using syntax like
-this:
-
-```yaml
-implementations:
- libraryname: String param_name1=param_value1 param_name2=param_value2
-```
-
-There are a few special parameters (written in ALL CAPS) that can be used here:
-
-
-
-
Parameter
Purpose
-
-
`DEGREES`
A list of degrees for which this element is defined in this implementation, eg `1,2,4:8` denotes that degrees 1 and 2 and 4 to 8 (including 4 but not including 8) are supported. Note that if `DEGREEMAP` is set, the degrees used here should correspond to the DefElement convention, not the degree after the degree map is applied.
-
`DEGREEMAP`
A map to apply to the degree used on DefElement to obtain the degree used by this library. The value here will be parsed using Sympy, with the variable `k` equal to the degree (using DefElement's convention).
-
-
-### Testing your contribution
-When you open a pull request, a series of tests and style checks will run via Github Actions.
-(You may have to wait for manual approval for these to run.)
-These tests and checks must pass before the pull request can be merged.
-If the tests and checks fail, you can click on them on the pull request page to see where the failure is happening.
-
-The style checks will check that the Python scripts that generate DefElement pass flake8 checks.
-If you've changed these scripts, you can run these checks locally by running:
-
-```bash
-python3 -m flake8 defelement build.py test
-```
-
-Before you can run the tests or do a test build, you'll need to install DefElement's requirements:
-
-```bash
-python3 -m pip install -r requirements.txt
-```
-
-The DefElement tests can be run using:
-
-```bash
-python3 -m pytest test/
-```
-
-To test that DefElement successfully builds, you can pass `--test auto` to the `build.py` script.
-This will build the website including examples for a small set of elements, and will take much less time
-then building the full website.
-
-```bash
-python3 build.py _test_html --test auto --processes 4
-```
-
-If you've updated an element, then you can test this element by replacing `auto` with the filename of the element you have edited.
-If you've updated multiple elements, you can use multiple filenames separated by commas. For example:
-
-```bash
-python3 build.py _test_html --test dpc --processes 4
-python3 build.py _test_html --test lagrange,vector-lagrange --processes 4
-```
-
-### Adding an implementation
-To add a library to the implementations section of DefElement, you must add a file containing to the folder
-[`/defelement/implementations`](https://github.com/DefElement/DefElement/blob/main/defelement/implementations).
-This file should define a class that is a subclass of [`Implementation`](https://github.com/DefElement/DefElement/blob/main/defelement/implementations/template.py).
-This class should include:
-
-
-
-
Item
Type
Use
-
-
`format`
method
This method should take an implementation string and a set of parameters as inputs and return the implementation information for the library, as it will be displayed on each element's page.
-
`example_import`
method
This method should return the imports to include at the start of a Python example code.
-
`single_example`
method
This method should take an element name, reference, degree, and parameters and return a block of Python (as a string) that creates the example element using the library. A DefElement element and the example string are additionally passed into this function in case they are needed by the function.
-
`version`
method
This method should return the version number of the implementation library.
-
`verify`
method (optional)
This method should take an element name, reference, degree, and parameters, and a set of points as inputs and returns the element for that example tabulated at the set of points and the number of DOFs associated with each sub-entity as a tuple of tuples. The shape of the first output is `(number of points, value size, number of basis functions)`. These functions are used to [verify](https://defelement.org/verification.html) that the implementation spans the same space as Symfem. A DefElement element and the example string are additionally passed into this function in case they are needed by the function.
-
`notes`
method (optional)
This method should take a DefElement `Element` object and return a list of notes about the implementation to include on the element's page.
-
`references`
method (optional)
This method should take a DefElement `Element` object and return a list of references relevant to the implementation to include on the element's page.
-
`id`
variable
The unique identifier for your library. This will be used in .def files.
-
`name`
variable
The name of your library.
-
`install`
variable
Code snippet to install you library (preferably using `pip3`)
-
`url`
variable
URL where the source code of your library is avaliable (eg a Github link).
-
`verification`
variable (optional)
Should be set to `True` if the `verify` function is implemented.
-
-
-Once this is done, you can start adding implementation details for your library to
-the `implementation` field of elements in the [`elements`](https://github.com/DefElement/DefElement/blob/main/elements)
-folder. The [adding an implementation to DefElement walkthough](https://defelement.org/adding-an-implementation.html)
-is a more detailed guide to the steps involved in adding an implementation.
-
-## Style guide
-When contributing to DefElement, you should follow [the DefElement style guide](https://defelement.org/style-guide.html).
-
-## Adding yourself to the contributors list
-Once you have contributed to DefElement, you should add your name and some information about yourself to the [contributors page](https://defelement.org/contributors.html).
-To do this, you should add info about yourself to the file [data/contributors](https://github.com/DefElement/DefElement/blob/main/data/contributors). If you wish to include a picture of yourself,
-add a square-shaped image to the [pictures/](https://github.com/DefElement/DefElement/blob/main/pictures/) folder.
-
-## Code of conduct
-We expect all our contributors to follow our [code of conduct](CODE_OF_CONDUCT.md). Any unacceptable
-behaviour can be reported to Matthew (defelement@mscroggs.co.uk).
-
-## AI-assisted development
-If a large language model (LLM) or other AI tool is used, this must be declared in the text of the pull request. Any AI-generated content must be thoroughly
-checked by the person opening the pull request before it is opened.
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index ea80d6b846..0000000000
--- a/LICENSE
+++ /dev/null
@@ -1,22 +0,0 @@
-MIT (defelement/, templates/, test/, build.py, verification.py, install_implementations.py)
-
-MIT License
-Copyright (c) 2020 Matthew Scroggs & other contributors to DefElement
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/LICENSE-CC b/LICENSE-CC
deleted file mode 100644
index 585c30056b..0000000000
--- a/LICENSE-CC
+++ /dev/null
@@ -1,399 +0,0 @@
-CC-BY-4.0 (data/, element/, files/ [excluding files/fontawesome], img/, pages/, people/)
-
-Attribution 4.0 International
-Attribution: Matthew Scroggs & other contributors to DefElement
-
-=======================================================================
-
-Creative Commons Corporation ("Creative Commons") is not a law firm and
-does not provide legal services or legal advice. Distribution of
-Creative Commons public licenses does not create a lawyer-client or
-other relationship. Creative Commons makes its licenses and related
-information available on an "as-is" basis. Creative Commons gives no
-warranties regarding its licenses, any material licensed under their
-terms and conditions, or any related information. Creative Commons
-disclaims all liability for damages resulting from their use to the
-fullest extent possible.
-
-Using Creative Commons Public Licenses
-
-Creative Commons public licenses provide a standard set of terms and
-conditions that creators and other rights holders may use to share
-original works of authorship and other material subject to copyright
-and certain other rights specified in the public license below. The
-following considerations are for informational purposes only, are not
-exhaustive, and do not form part of our licenses.
-
- Considerations for licensors: Our public licenses are
- intended for use by those authorized to give the public
- permission to use material in ways otherwise restricted by
- copyright and certain other rights. Our licenses are
- irrevocable. Licensors should read and understand the terms
- and conditions of the license they choose before applying it.
- Licensors should also secure all rights necessary before
- applying our licenses so that the public can reuse the
- material as expected. Licensors should clearly mark any
- material not subject to the license. This includes other CC-
- licensed material, or material used under an exception or
- limitation to copyright. More considerations for licensors:
- wiki.creativecommons.org/Considerations_for_licensors
-
- Considerations for the public: By using one of our public
- licenses, a licensor grants the public permission to use the
- licensed material under specified terms and conditions. If
- the licensor's permission is not necessary for any reason--for
- example, because of any applicable exception or limitation to
- copyright--then that use is not regulated by the license. Our
- licenses grant only permissions under copyright and certain
- other rights that a licensor has authority to grant. Use of
- the licensed material may still be restricted for other
- reasons, including because others have copyright or other
- rights in the material. A licensor may make special requests,
- such as asking that all changes be marked or described.
- Although not required by our licenses, you are encouraged to
- respect those requests where reasonable. More considerations
- for the public:
- wiki.creativecommons.org/Considerations_for_licensees
-
-=======================================================================
-
-Creative Commons Attribution 4.0 International Public License
-
-By exercising the Licensed Rights (defined below), You accept and agree
-to be bound by the terms and conditions of this Creative Commons
-Attribution 4.0 International Public License ("Public License"). To the
-extent this Public License may be interpreted as a contract, You are
-granted the Licensed Rights in consideration of Your acceptance of
-these terms and conditions, and the Licensor grants You such rights in
-consideration of benefits the Licensor receives from making the
-Licensed Material available under these terms and conditions.
-
-
-Section 1 -- Definitions.
-
- a. Adapted Material means material subject to Copyright and Similar
- Rights that is derived from or based upon the Licensed Material
- and in which the Licensed Material is translated, altered,
- arranged, transformed, or otherwise modified in a manner requiring
- permission under the Copyright and Similar Rights held by the
- Licensor. For purposes of this Public License, where the Licensed
- Material is a musical work, performance, or sound recording,
- Adapted Material is always produced where the Licensed Material is
- synched in timed relation with a moving image.
-
- b. Adapter's License means the license You apply to Your Copyright
- and Similar Rights in Your contributions to Adapted Material in
- accordance with the terms and conditions of this Public License.
-
- c. Copyright and Similar Rights means copyright and/or similar rights
- closely related to copyright including, without limitation,
- performance, broadcast, sound recording, and Sui Generis Database
- Rights, without regard to how the rights are labeled or
- categorized. For purposes of this Public License, the rights
- specified in Section 2(b)(1)-(2) are not Copyright and Similar
- Rights.
-
- d. Effective Technological Measures means those measures that, in the
- absence of proper authority, may not be circumvented under laws
- fulfilling obligations under Article 11 of the WIPO Copyright
- Treaty adopted on December 20, 1996, and/or similar international
- agreements.
-
- e. Exceptions and Limitations means fair use, fair dealing, and/or
- any other exception or limitation to Copyright and Similar Rights
- that applies to Your use of the Licensed Material.
-
- f. Licensed Material means the artistic or literary work, database,
- or other material to which the Licensor applied this Public
- License.
-
- g. Licensed Rights means the rights granted to You subject to the
- terms and conditions of this Public License, which are limited to
- all Copyright and Similar Rights that apply to Your use of the
- Licensed Material and that the Licensor has authority to license.
-
- h. Licensor means the individual(s) or entity(ies) granting rights
- under this Public License.
-
- i. Share means to provide material to the public by any means or
- process that requires permission under the Licensed Rights, such
- as reproduction, public display, public performance, distribution,
- dissemination, communication, or importation, and to make material
- available to the public including in ways that members of the
- public may access the material from a place and at a time
- individually chosen by them.
-
- j. Sui Generis Database Rights means rights other than copyright
- resulting from Directive 96/9/EC of the European Parliament and of
- the Council of 11 March 1996 on the legal protection of databases,
- as amended and/or succeeded, as well as other essentially
- equivalent rights anywhere in the world.
-
- k. You means the individual or entity exercising the Licensed Rights
- under this Public License. Your has a corresponding meaning.
-
-
-Section 2 -- Scope.
-
- a. License grant.
-
- 1. Subject to the terms and conditions of this Public License,
- the Licensor hereby grants You a worldwide, royalty-free,
- non-sublicensable, non-exclusive, irrevocable license to
- exercise the Licensed Rights in the Licensed Material to:
-
- a. reproduce and Share the Licensed Material, in whole or
- in part; and
-
- b. produce, reproduce, and Share Adapted Material.
-
- 2. Exceptions and Limitations. For the avoidance of doubt, where
- Exceptions and Limitations apply to Your use, this Public
- License does not apply, and You do not need to comply with
- its terms and conditions.
-
- 3. Term. The term of this Public License is specified in Section
- 6(a).
-
- 4. Media and formats; technical modifications allowed. The
- Licensor authorizes You to exercise the Licensed Rights in
- all media and formats whether now known or hereafter created,
- and to make technical modifications necessary to do so. The
- Licensor waives and/or agrees not to assert any right or
- authority to forbid You from making technical modifications
- necessary to exercise the Licensed Rights, including
- technical modifications necessary to circumvent Effective
- Technological Measures. For purposes of this Public License,
- simply making modifications authorized by this Section 2(a)
- (4) never produces Adapted Material.
-
- 5. Downstream recipients.
-
- a. Offer from the Licensor -- Licensed Material. Every
- recipient of the Licensed Material automatically
- receives an offer from the Licensor to exercise the
- Licensed Rights under the terms and conditions of this
- Public License.
-
- b. No downstream restrictions. You may not offer or impose
- any additional or different terms or conditions on, or
- apply any Effective Technological Measures to, the
- Licensed Material if doing so restricts exercise of the
- Licensed Rights by any recipient of the Licensed
- Material.
-
- 6. No endorsement. Nothing in this Public License constitutes or
- may be construed as permission to assert or imply that You
- are, or that Your use of the Licensed Material is, connected
- with, or sponsored, endorsed, or granted official status by,
- the Licensor or others designated to receive attribution as
- provided in Section 3(a)(1)(A)(i).
-
- b. Other rights.
-
- 1. Moral rights, such as the right of integrity, are not
- licensed under this Public License, nor are publicity,
- privacy, and/or other similar personality rights; however, to
- the extent possible, the Licensor waives and/or agrees not to
- assert any such rights held by the Licensor to the limited
- extent necessary to allow You to exercise the Licensed
- Rights, but not otherwise.
-
- 2. Patent and trademark rights are not licensed under this
- Public License.
-
- 3. To the extent possible, the Licensor waives any right to
- collect royalties from You for the exercise of the Licensed
- Rights, whether directly or through a collecting society
- under any voluntary or waivable statutory or compulsory
- licensing scheme. In all other cases the Licensor expressly
- reserves any right to collect such royalties.
-
-
-Section 3 -- License Conditions.
-
-Your exercise of the Licensed Rights is expressly made subject to the
-following conditions.
-
- a. Attribution.
-
- 1. If You Share the Licensed Material (including in modified
- form), You must:
-
- a. retain the following if it is supplied by the Licensor
- with the Licensed Material:
-
- i. identification of the creator(s) of the Licensed
- Material and any others designated to receive
- attribution, in any reasonable manner requested by
- the Licensor (including by pseudonym if
- designated);
-
- ii. a copyright notice;
-
- iii. a notice that refers to this Public License;
-
- iv. a notice that refers to the disclaimer of
- warranties;
-
- v. a URI or hyperlink to the Licensed Material to the
- extent reasonably practicable;
-
- b. indicate if You modified the Licensed Material and
- retain an indication of any previous modifications; and
-
- c. indicate the Licensed Material is licensed under this
- Public License, and include the text of, or the URI or
- hyperlink to, this Public License.
-
- 2. You may satisfy the conditions in Section 3(a)(1) in any
- reasonable manner based on the medium, means, and context in
- which You Share the Licensed Material. For example, it may be
- reasonable to satisfy the conditions by providing a URI or
- hyperlink to a resource that includes the required
- information.
-
- 3. If requested by the Licensor, You must remove any of the
- information required by Section 3(a)(1)(A) to the extent
- reasonably practicable.
-
- 4. If You Share Adapted Material You produce, the Adapter's
- License You apply must not prevent recipients of the Adapted
- Material from complying with this Public License.
-
-
-Section 4 -- Sui Generis Database Rights.
-
-Where the Licensed Rights include Sui Generis Database Rights that
-apply to Your use of the Licensed Material:
-
- a. for the avoidance of doubt, Section 2(a)(1) grants You the right
- to extract, reuse, reproduce, and Share all or a substantial
- portion of the contents of the database;
-
- b. if You include all or a substantial portion of the database
- contents in a database in which You have Sui Generis Database
- Rights, then the database in which You have Sui Generis Database
- Rights (but not its individual contents) is Adapted Material; and
-
- c. You must comply with the conditions in Section 3(a) if You Share
- all or a substantial portion of the contents of the database.
-
-For the avoidance of doubt, this Section 4 supplements and does not
-replace Your obligations under this Public License where the Licensed
-Rights include other Copyright and Similar Rights.
-
-
-Section 5 -- Disclaimer of Warranties and Limitation of Liability.
-
- a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
- EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
- AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
- ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
- IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
- WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
- PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
- ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
- KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
- ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
-
- b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
- TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
- NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
- INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
- COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
- USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
- ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
- DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
- IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
-
- c. The disclaimer of warranties and limitation of liability provided
- above shall be interpreted in a manner that, to the extent
- possible, most closely approximates an absolute disclaimer and
- waiver of all liability.
-
-
-Section 6 -- Term and Termination.
-
- a. This Public License applies for the term of the Copyright and
- Similar Rights licensed here. However, if You fail to comply with
- this Public License, then Your rights under this Public License
- terminate automatically.
-
- b. Where Your right to use the Licensed Material has terminated under
- Section 6(a), it reinstates:
-
- 1. automatically as of the date the violation is cured, provided
- it is cured within 30 days of Your discovery of the
- violation; or
-
- 2. upon express reinstatement by the Licensor.
-
- For the avoidance of doubt, this Section 6(b) does not affect any
- right the Licensor may have to seek remedies for Your violations
- of this Public License.
-
- c. For the avoidance of doubt, the Licensor may also offer the
- Licensed Material under separate terms or conditions or stop
- distributing the Licensed Material at any time; however, doing so
- will not terminate this Public License.
-
- d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
- License.
-
-
-Section 7 -- Other Terms and Conditions.
-
- a. The Licensor shall not be bound by any additional or different
- terms or conditions communicated by You unless expressly agreed.
-
- b. Any arrangements, understandings, or agreements regarding the
- Licensed Material not stated herein are separate from and
- independent of the terms and conditions of this Public License.
-
-
-Section 8 -- Interpretation.
-
- a. For the avoidance of doubt, this Public License does not, and
- shall not be interpreted to, reduce, limit, restrict, or impose
- conditions on any use of the Licensed Material that could lawfully
- be made without permission under this Public License.
-
- b. To the extent possible, if any provision of this Public License is
- deemed unenforceable, it shall be automatically reformed to the
- minimum extent necessary to make it enforceable. If the provision
- cannot be reformed, it shall be severed from this Public License
- without affecting the enforceability of the remaining terms and
- conditions.
-
- c. No term or condition of this Public License will be waived and no
- failure to comply consented to unless expressly agreed to by the
- Licensor.
-
- d. Nothing in this Public License constitutes or may be interpreted
- as a limitation upon, or waiver of, any privileges and immunities
- that apply to the Licensor or You, including from the legal
- processes of any jurisdiction or authority.
-
-
-=======================================================================
-
-Creative Commons is not a party to its public
-licenses. Notwithstanding, Creative Commons may elect to apply one of
-its public licenses to material it publishes and in those instances
-will be considered the “Licensor.” The text of the Creative Commons
-public licenses is dedicated to the public domain under the CC0 Public
-Domain Dedication. Except for the limited purpose of indicating that
-material is shared under a Creative Commons public license or as
-otherwise permitted by the Creative Commons policies published at
-creativecommons.org/policies, Creative Commons does not authorize the
-use of the trademark "Creative Commons" or any other trademark or logo
-of Creative Commons without its prior written consent including,
-without limitation, in connection with any unauthorized modifications
-to any of its public licenses or any other arrangements,
-understandings, or agreements concerning use of licensed material. For
-the avoidance of doubt, this paragraph does not form part of the
-public licenses.
-
-Creative Commons may be contacted at creativecommons.org.
-
diff --git a/LICENSE-FA b/LICENSE-FA
deleted file mode 100644
index dc0fd7916b..0000000000
--- a/LICENSE-FA
+++ /dev/null
@@ -1,3 +0,0 @@
-Font Awesome Free License (files/fontawesome/)
-
-See files/fontawesome/LICENSE.txt.
diff --git a/README.md b/README.md
deleted file mode 100644
index e7ef2e24a5..0000000000
--- a/README.md
+++ /dev/null
@@ -1,43 +0,0 @@
-# DefElement
-
-This repo contains code to generate the website
-[DefElement: an encylopedia of finite element definitions](https://defelement.org).
-
-The examples included in DefElement are generated using [Symfem](https://github.com/mscroggs/symfem).
-
-## Building the website
-
-Before building the website, you must install the required Python dependencies:
-
-```bash
-pip3 install -r requirements.txt
-```
-
-The html files for the website can be built by running:
-
-```bash
-python build.py
-```
-
-A version of the website where only some elements are plotted can be built by using the
-`--test` input arg. For example, the following can be run to build the website with only
-plots for Lagrange and Raviart-Thomas (`lagrange` and `raviart-thomas` are
-the filenames of the `.def` files in the `elements` folder that define these
-elements):
-
-```bash
-python build.py --test lagrange,raviart-thomas
-```
-
-## Licensing
-
-The code to generate and test the DefElement website (`defelement/`, `templates/`, `test/`, `build.py`, `verify.py`, `install_implementations.py`)
-is released under an [MIT license](LICENSE.txt).
-
-The content of the DefElement website itself (including `data/`, `elements/`, `files/`, `pages/`, `people/`)
-is released under a [Creative Commons Attribution 4.0 International (CC BY 4.0) license](LICENSE-CC.txt).
-
-Font Awesome (`files/fontawesome`) is released under a [Font Awesome Free License](files/fontawesome/LICENSE.txt).
-
-## Archive
-Archived snapshots of code for building DefElement and website builds can be found at https://doi.org/10.5281/zenodo.17904468
diff --git a/build.py b/build.py
deleted file mode 100644
index 89cb27b777..0000000000
--- a/build.py
+++ /dev/null
@@ -1,1751 +0,0 @@
-"""Build DefElement website."""
-
-import argparse
-import json
-import os
-import typing
-from datetime import datetime, timedelta, timezone
-
-import symfem
-from symfem import create_element
-from webtools.citations import make_bibtex, markup_citation
-from webtools.html import make_html_forwarding_page, make_html_page
-from webtools.markup import cap_first, heading, heading_with_self_ref, markup
-from webtools.tools import (
- comma_and_join,
- html_local,
- insert_author_info,
- parse_metadata,
-)
-
-from defelement import plotting, settings
-from defelement.code_examples import generate_examples
-from defelement.element import Categoriser
-from defelement.examples import markup_example
-from defelement.families import keys_and_names
-from defelement.implementations import implementations, parse_example, verifications
-from defelement.markup import insert_links
-from defelement.rss import make_rss
-from defelement.tools import jsify
-
-
-def mkdir(folder):
- """Make a directory if it doesn't exist."""
- try:
- os.mkdir(folder)
- except FileExistsError:
- pass
-
-
-def build_example(eg: dict[str, typing.Any]):
- """Build examples.
-
- Args:
- eg: Example
- """
- start = datetime.now(tz=timezone(timedelta()))
-
- element = create_element(*eg["args"], **eg["kwargs"])
-
- markup_example(
- element,
- eg["html_name"],
- f"/elements/{eg['element_filename']}",
- eg["filename"],
- eg.get("legacy-filenames", []),
- )
-
- end = datetime.now(tz=timezone(timedelta()))
- print(
- f" {eg['args'][0]} {eg['args'][1]} {eg['args'][2]}"
- f" (completed in {(end - start).total_seconds():.2f}s)",
- flush=True,
- )
-
-
-if __name__ == "__main__":
- start_all = datetime.now(tz=timezone(timedelta()))
-
- parser = argparse.ArgumentParser(description="Build defelement.org")
- parser.add_argument(
- "destination",
- metavar="destination",
- nargs="?",
- default=None,
- help="Destination of HTML files.",
- )
- parser.add_argument(
- "--test",
- metavar="test",
- default=None,
- help="Builds a version of the website with fewer elements.",
- )
- parser.add_argument(
- "--processes",
- metavar="processes",
- default=None,
- help="The number of processes to run the building of examples on.",
- )
- parser.add_argument(
- "--verification-json",
- metavar="verification_json",
- default=None,
- help="Provide a verification JSON.",
- )
- parser.add_argument("--no-cache", action="store_true", help="Build without using cache.")
- parser.add_argument(
- "--include-simplefem", action="store_true", help="Include simplefem on all pages."
- )
-
- sitemap = {}
-
- def write_html_page(
- path: str,
- title: str | None,
- content: str,
- extra_head: str | None = None,
- ):
- """Write a HTML page.
-
- Args:
- path: Page path
- title: Page title
- content: Page content
- extra_head: Extra HTML to include inside
- """
- assert html_local(path) not in sitemap
- if title is not None:
- sitemap[html_local(path)] = title
- with open(path, "w") as f:
- f.write(make_html_page(content, title, extra_head=extra_head))
-
- args = parser.parse_args()
- if args.destination is not None:
- settings.set_html_path(args.destination)
-
- if args.no_cache:
- settings.caching = False
-
- if args.processes is not None:
- settings.set_processes(int(args.processes))
-
- if "GITHUB_TOKEN" in os.environ:
- settings.set_github_token(os.environ["GITHUB_TOKEN"])
-
- if args.verification_json is not None:
- settings.set_verification_json(args.verification_json)
-
- if args.test is None:
- test_elements = None
- elif args.test == "auto":
- test_elements = [
- "buffa-christiansen",
- "direct-serendipity",
- "dual",
- "hellan-herrmann-johnson",
- "hsieh-clough-tocher",
- "lagrange",
- "nedelec1",
- "raviart-thomas",
- "regge",
- "serendipity",
- "taylor-hood",
- "vector-bubble-enriched-Lagrange",
- "enriched-galerkin",
- ]
- else:
- test_elements = args.test.split(",")
-
- include_simplefem = args.include_simplefem
-
- # Prepare paths
- if os.path.isdir(settings.html_path):
- os.system(f"rm -rf {settings.html_path}")
- mkdir(settings.html_path)
- mkdir(settings.htmlelement_path)
- mkdir(settings.htmlindices_path)
- mkdir(settings.htmlfamilies_path)
- mkdir(settings.htmlimg_path)
- mkdir(os.path.join(settings.html_path, "badges"))
-
- os.system(f"cp -r {settings.dir_path}/people {settings.htmlimg_path}")
- mkdir(os.path.join(settings.htmlelement_path, "bibtex"))
- mkdir(os.path.join(settings.htmlelement_path, "examples"))
-
- os.system(f"cp -r {settings.files_path}/* {settings.html_path}")
-
- with open(os.path.join(settings.html_path, "CNAME"), "w") as f:
- f.write("defelement.org")
-
- # Load categories and reference cells
- categoriser = Categoriser()
- categoriser.load_categories(os.path.join(settings.data_path, "categories"))
- categoriser.load_references(os.path.join(settings.data_path, "references"))
- categoriser.load_families(os.path.join(settings.data_path, "families"))
-
- # Make pages
- for file in os.listdir(settings.pages_path):
- if file.endswith(".md"):
- start = datetime.now(tz=timezone(timedelta()))
- fname = file[:-3]
- print(f"{fname}.html", end="", flush=True)
- with open(os.path.join(settings.pages_path, file)) as f:
- metadata, content = parse_metadata(f.read())
-
- if "redirect" in metadata:
- with open(os.path.join(settings.html_path, f"{fname}.html"), "w") as f:
- f.write(
- make_html_forwarding_page(
- "/" + metadata["redirect"].replace(".md", ".html")
- )
- )
- continue
-
- if "authors" in metadata:
- content = insert_author_info(content, metadata["authors"], f"{fname}.html")
-
- content = markup(content)
-
- if "{{REFERENCE_CELL_NUMBERING}}" in content:
- reference_cell_numbering = ""
- for cell in categoriser.references:
- if cell == "dual polygon":
- for nsides in [4, 5, 6]:
- reference_cell_numbering += heading_with_self_ref(
- "h2", f"Dual polygon ({nsides})"
- )
- reference_cell_numbering += plotting.plot_reference(
- symfem.create_reference(f"dual polygon({nsides})")
- )
- else:
- reference_cell_numbering += heading_with_self_ref("h2", cap_first(cell))
- reference_cell_numbering += plotting.plot_reference(
- symfem.create_reference(cell)
- )
- content = content.replace("{{REFERENCE_CELL_NUMBERING}}", reference_cell_numbering)
-
- write_html_page(
- os.path.join(settings.html_path, f"{fname}.html"),
- metadata["title"],
- content,
- )
- end = datetime.now(tz=timezone(timedelta()))
- print(f" (completed in {(end - start).total_seconds():.2f}s)")
-
- # Load elements from .def files
- categoriser.load_folder(settings.element_path)
-
- cdescs = {
- "L2": "Discontinuous.",
- "H1": "Function values are continuous.",
- "H2": "Function values and derivatives are continuous.",
- "H3": "Function values and first and second derivatives are continuous.",
- "H(div)": "Components normal to facets are continuous",
- "H(curl)": "Components tangential to facets are continuous",
- "H(div div)": "Inner products with normals to facets are continuous",
- "H(curl curl)": "Inner products with tangents to facets are continuous",
- "H(curl div)": "Tangent-normal inner products on facets are continuous",
- "H1(div)": "Function values and divergence are continuous.",
- }
-
- VHistoryDict = typing.TypedDict(
- "VHistoryDict",
- {"date": str, "pass": int, "total": int, "version": str},
- total=False,
- )
-
- verification: dict[str, dict[str, dict[str, list[str]]]] = {}
- vhistory: dict[str, list[VHistoryDict]] = {}
-
- v_date = None
- if os.path.isfile(settings.verification_json):
- with open(settings.verification_json) as f:
- v_json = json.load(f)
- verification = v_json["verification"]
- v_date = v_json["metadata"]["date"]
- if os.path.isfile(settings.verification_history_json):
- with open(settings.verification_history_json) as f:
- vhistory = json.load(f)
-
- icon_style = "font-size:150%;vertical-align:middle"
- icon_style_small = "font-size:80%;vertical-align:middle"
- text_style = "font-size:80%;vertical-align:middle"
- green_check = (
- ""
- )
- orange_check = (
- ""
- )
- red_check = f""
- blue_minus = (
- ""
- )
- green_check_small = green_check.replace(icon_style, icon_style_small)
- orange_check_small = orange_check.replace(icon_style, icon_style_small)
- red_check_small = red_check.replace(icon_style, icon_style_small)
- blue_minus_small = blue_minus.replace(icon_style, icon_style_small)
-
- # Generate element pages
- all_examples = []
- for e in categoriser.elements:
- print(e.name)
- content = heading_with_self_ref("h1", cap_first(e.html_name))
- element_data = []
- impl = []
-
- # Link to ciarlet.html
- content += "
"
-
- # Write created and updated dates
- if e.created is not None:
- content += heading_with_self_ref("h2", "DefElement stats")
- content += "
"
- content += (
- f"
Element added
{e.created.strftime('%d %B %Y')}
"
- )
- content += "
Element last updated
"
- content += f"
{e.modified.strftime('%d %B %Y')}
"
- content += "
"
-
- # Write file
- write_html_page(
- os.path.join(settings.htmlelement_path, e.html_filename), e.html_name, content
- )
-
- # Make redirects from legacy filenames
- for fname in e.legacy_filenames:
- with open(os.path.join(settings.htmlelement_path, f"{fname}.html"), "w") as f:
- f.write(make_html_forwarding_page(f"/elements/{e.html_filename}"))
-
- # Verification badges
- img = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIIAAACCCAYAAACKAxD9AAAABHNCSVQICAgIfAhkiAAACiVJREFUeJztnXmwHFUVxn8nDyEkLCoSkzLsFiIGAhHKINkw7siiCSpRiFUohKJUpBSkCgkkiohSllBKAC0FZJUXWaXQgpQBEoISAqUECYssYScYJXkhy/v8o3se8+YtM3P7znS/mfOrelU9031PfzPzvdv39j19LziO4zhOL2ywndJmoONAYDzwjqYoGhhL/54FXgM2AquA9WaDfgynBgb8BiVNB34O7Nc8OUFsIDHEk8DjwEPAcjN7PFdVrYCkb0jq1tDmVUkLJX1H0l6dnZ15f62Fpk+NIGkSsLi/fUOclcCNwPVm9s+8xRSNXj+2JIDlwIG5qGkeDwILgGvMbH3eYopApRH2Bdrpv+V14CLgF2a2Nm8xeTKs4vUhuajIj52Ac4HVkuZK2i5vQXlRaYTK1+3CSOAcYJWkWeklsq1o1x9+IEYDVwN3S9orbzHNxI3QP4cBD0ua0y61gxthYEYClwB/lLR93mIajRuhOkcDj0gq+h3WTLgRamN34H5Jn8lbSKPYKlKc80gGgxqFgB2BXYDtgX2APYFRDTxnJSOA2ySdYmYLmnjephDLCJ1mtjxSrJpJ+/0TgIOBScBkknsDjWIYcImk7czsZw08T74oGWwKYULe2gEkmaSDJP1Q0hOBn6VWvpv3520YGuJGKGfWrFlI+oikBZLWRfv5e/OtvD9nQ1ALGaEcSTtKOkPJ0HRsTsj788WgLXoNZrbWzH5C0vo/H3grYviLJU2OGC8X2sIIJcxsnZmdCXwQuCVS2G2BGyXtEileLsTqNRQabdkCw4ZNAx41s1fM7GlJRwFfAX5J0jXNwijgWklTzWyLpOHADcDOGePG4g2SxJyFc+fOvW/evHmDH60WbCNI2kXSolTnGkmnSeoo2z9W0v3hTYRezC+LOyNSzNjcImlw46vFjCBpjKR/96P3AUn7lB23taTLAj97OZsljS+L+4cIMRvBCkkjyr+rlm0jKBk1vArYrZ/dBwPLJZ2ycuVKzGyjmZ0InJ7xtB3AVZJKqf+nAkVMhRsPXDjgXrVQjSBpao3afydpm7JyJwV+B+WcVhZvXoR4jaBL0rtLOlu2RgCOqvG42cBfJI0EMLNLgTkZz32OpFJD8QJgTcZ4jWA4MKX0opWNsH8dx04GFpd+vNQMF2Q49/bA/DTWm0BRxyV6Bu1a2QjD6zx+ArBQbzeizgBuynD+EyTtmm5fTJIxXTR60q9a2QghTAJukmTp85RfJXmULoStgDOhp1a4PIrCuCwtbbgR+vIJ4MeQ3IkEvkjywG0IX5NUqn4vpFg9iIeAf5ReuBH65wxJnwZI8yzOC4wzHPhmGuc14Po48jKzCTi5/ClyN8LAXFnR8l8VGOdEJbecAS7LLisz/wVmmNmy8jfdCAOzM2lr38y6gO8FxhkFHJlu3w/8K7u0IJ4jebxvXzO7tXJnWww61cEG4F397TCzmyXdQ9LVrJfjgRvMDCXZ0B3VCkTmrREjRqirq2vAA9wIFZjZhkF2zwXuDgh7uKTRZvaSmW0iuUYXCr801Mci4IHAsjNiComNG6EO0lb2rwKL13rLOxfcCPVzPUnLu14+LumdscXEwo1QJ2kb4rqQosC0uGri4UYII3RmrsImuboRwlhE2O3i6bGFxMKNEEDaBVwcUHSc3s5eKhRuhHBCjNABHBBbSAzcCOEsrX5Iv4yLqiISboRwVgSWqydzqmm4EQIxs/8ALwYU7S+rOnfcCNl4IqDMHtFVRMCNkI0QIxTyGUk3QjbeCCizkwo4ZZ8bIRvPBZbbNqqKCLgRshGajDomqooIuBGyEbqmReFGId0IDuBGcFLcCA7gRshKd2C5Z6KqiIAbIRujA8sVbtkgN0I2xgaW2xxVRQTcCNkImY3tsSKuXOtGyMY+1Q/pQxFnT3EjhJKOF+wdULSQSxW7EcLZm7AxAzdCixG6Wm5eT0MPihshnEMDy4WmuDUUN0I4HwsoswZ4KraQGLgRApA0GvhQQNGlRew6ghuhkuGDzFT6SNlxxwbGv6e0IemxhsynKr0s6TZJn6tHmBuhdsqnx5sZGOPPAOlMax/IrKh/RgGHA7dKulFSTT0bnzGlNv4HXAkgaQ/gowExXiKZ0g6a9zDsDKBD0uerXZK8RqiNq82sNFB0SmCM28t+jC9ll1QzRwNfrnaQG6E6m4CfQs86k6GLeS1MY3SQLELeTKpqdiNU53IzK3X5TiIs3/Bl4M50+5PAe2IIq4Op1VLo3QiDs4Z0PuW0Nvh+YJxrzWxLuj0rhrA6qfoovhthcE41s9J8SecS/p98GYCkHchnUq2qafduhIH507Jly64CkLQv6ZzKAfzVzFam218gWcuh2SzxXkMYzwOzJ06ciCQDrqCG6nUALi3bzmsJ4aoLh7gR+tIFHJvOpg7wA+CgwFhPkM7AJmkS4SOWWfh1d3f3ndUOciP05WQzuxdA0iHA2RlinW9mpeb6WZmV1YdI1p04qaOj+tTPfmexNxvN7AoASbuT9P1DJ9B+muSSgpLlgncGlkfQWI31wBLgN2ZWcxKMG6E33QCHvg9IptYPTVcHONvMNgOY2XPAh7OKayR+aahg8li47rjMYf4Om38fQU7T8BqhgmuOg7FZlwyHOWaFnE5xQLxGqCCCCS4yswcjSGkqboS4PEV6S3qo4UaIy/FmVqQl/WrGjRCPs8zsvrxFhOJGiMNC4Ed5i8iCGyE7K4DjipqdXCtuhGw8AxwxVNsF5bgRwnkBmG5mz+ctJAYtYYRp06Yh6b2S9nv427BuXsNPuRqYYmahK8kXjiF1ZzHNu9sReD/J5NZ7A+OBCel7V+4/htkNlvEscFhZHmNLEMsIV0tq1HVS6d+uJA9v5MkK4Mh0EKmliGWEkJlDhhp3AMeY2bq8hTSClmgjNBgB84HPtqoJYIi1EXJgNTDbzO7KW0ijcSMMzFJgvJm9nreQZuCXhgrWbYQFS8DM/tYuJgCvEfrwqUvhvtV5q2g+lTVCyzaGaqUdTQB9jbAoFxVO7vQygpm9CAyppEsnDv01Fk8nuY3qtBF9jJDWClOAVc2Xk531m+DJtmnrx2PAbApJw4GvA8cAE4GtmyVqENaSrLX4OvAmSbLoWuBR4EmSmc9fAJB0L/VPirnBzAq3FJ+TAUn3BkxN15W37rzwG0oO4EZwUtwIDuBGcFLcCA7gg06VmKSReYtoAJtIJgEZ8AA3Qm+2Ibk/0Yq8KOlmYH7pXks5fmloH8YAc4CVkqZU7nQjtB87AHdIOqD8TTdCezICuGTcuHE9bwztJzcHIXCsod3Y08yeBq8R2p2e5QJa2Qhv5S1gCNDTa2xlIzxS/ZC2pydzo5WNcFveAoYAi0sbrWyEu0geUnH657dm9mrpRcv2GgAk7Uri+t3y1lIwVgEHlS1K0tI1Amb2LEn+pafpv82twCHlJoAWrxFKqLsbzKYCR5BMtNGOvALcPnPmzCWdnZ15a3Ecp9D8H6iRYL8kgknaAAAAAElFTkSuQmCC"
- badges = os.path.join(settings.html_path, "badges")
- for i in verifications:
- if i != "symfem" and not i.startswith("*("):
- good = 0
- total = 0
- for ver in verification.values():
- if i in ver:
- good += len(ver[i]["pass"])
- total += len(ver[i]["pass"]) + len(ver[i]["fail"])
- proportion = f"{good} / {total}"
- if good == total:
- col = symfem.plotting.Colors.GREEN
- elif good < total / 2:
- col = "#FF0000"
- else:
- col = symfem.plotting.Colors.ORANGE
- twidth = 50 + 70 * (len(proportion) - 2)
- width = 840 + 90 + twidth
- svgwidth = width // 10 if width % 10 == 0 else width / 10
- with open(os.path.join(badges, f"{i}.svg"), "w") as f:
- f.write(
- f'"
- )
- with open(os.path.join(badges, "symfem.svg"), "w") as f:
- f.write(
- f'"
- )
-
- # Make verification pages
- mkdir(os.path.join(settings.html_path, "verification"))
-
- impl_content = {i: "" for i in verifications if i != "symfem" and not i.startswith("*(")}
- for i in impl_content:
- title = f"{implementations[i].name} verification"
- if i in vhistory:
- good = vhistory[i][-1]["pass"]
- total = vhistory[i][-1]["total"]
- if good == total:
- col = symfem.plotting.Colors.GREEN
- elif good < total / 2:
- col = "#FF0000"
- else:
- col = symfem.plotting.Colors.ORANGE
- title += (
- "{good} / {total}"
- )
- impl_content[i] += heading("h1", title)
-
- content = heading_with_self_ref("h1", "Verification")
- long_content = heading_with_self_ref("h1", "Verification: full detail")
- if v_date is not None:
- year, month, day = [int(i) for i in v_date.split("-")]
- monthname = [
- "Zeromber",
- "January",
- "February",
- "March",
- "April",
- "May",
- "June",
- "July",
- "August",
- "September",
- "October",
- "November",
- "December",
- ][month]
- updated = f"Last updated: {day} {monthname} {year}
"
- content += updated
- long_content += updated
- for i in impl_content:
- impl_content[i] += updated
-
- for i in impl_content:
- if i in vhistory:
- hist = vhistory[i]
- impl_content[i] += (
- ""
- impl_content[i] += (
- "The plot above shows the number of elements passing verificiation (green line) "
- "out of the number of elements being verified (dashed black line) over time."
- )
- impl_content[i] += "
"
-
- content += "
"
- content += ""
- content += "
Element
"
- long_content += "
"
- long_content += ""
- long_content += "
Element
Example
"
- for i in impl_content:
- impl_content[i] += (
- "
"
- ""
- "
Element
Example
"
- ""
- )
- vs = []
- for i in verifications:
- if i != "symfem":
- if i.startswith("*(") and i.endswith(")"):
- continue
- vs.append(i)
- if not include_simplefem and i == "simplefem":
- continue
- content += f"
"
- for i in vs:
- if not include_simplefem and i == "simplefem":
- continue
- row += "
"
- if e.filename in verification and i in verification[e.filename]:
- result = verification[e.filename][i]
- if len(result["pass"]) > 0 or len(result["fail"]) > 0:
- n += 1
- if len(result["fail"]) == 0:
- row += green_check
- elif len(result["pass"]) > 0:
- row += orange_check
- else:
- row += red_check
- row += "
"
- row += "
"
-
- examples = []
- if e.filename in verification:
- for vv in verification[e.filename].values():
- for egs in vv.values():
- for eg in egs:
- if eg not in examples:
- examples.append(eg)
- sorted_examples = []
- for cell in [
- "interval",
- "triangle",
- "quadrilateral",
- "tetrahedron",
- "hexahedron",
- "prism",
- "pyramid",
- "dual",
- ]:
- sorted_examples += sorted(
- [i for i in examples if i.startswith(cell)],
- key=lambda i: ",".join(i.split(",")[:0:-1]),
- )
- assert len(examples) == len(sorted_examples)
- long_row = ""
- impl_rows: dict[str, list[str]] = {i: [] for i in impl_content}
- for eg in sorted_examples:
- long_row += "
"
- if e.filename in verification and i in verification[e.filename]:
- result = verification[e.filename][i]
- if eg in result["pass"]:
- impl_rows[i].append(
- f"
You can view a summarised version of this information on the "
- "verification page.
"
- )
- for i in impl_content:
- impl_content[i] += c
- impl_content[i] += (
- "
"
- f"
{green_check}
Verification passes
"
- f"
{red_check}
Verification fails
"
- "
"
- "
You can information about verification of other libraries on the "
- "verification page.
"
- )
- if os.path.isfile(settings.verification_json):
- os.system(f"cp {settings.verification_json} {settings.html_path}/verification.json")
- c = (
- "
The verification data is also available "
- "in JSON format.
"
- )
- content += c
- long_content += c
-
- c = heading_with_self_ref("h2", "Verification Github badges")
- c += "
"
- c += "
Implementation
Badge
Markdown
"
- for i in verifications:
- if i == "symfem":
- url = "https://defelement.org/verification/"
- else:
- url = f"https://defelement.org/verification/{i}.html"
- if i.startswith("*(") and i.endswith(")"):
- continue
- if i in impl_content:
- impl_content[i] += heading_with_self_ref("h2", "Verification Github badge")
- impl_content[i] += (
- "
"
- content += c
- long_content += c
- write_html_page(
- os.path.join(settings.html_path, "verification/index.html"),
- "Verification",
- markup(content),
- )
- write_html_page(
- os.path.join(settings.html_path, "verification/detailed.html"),
- "Verification: full detail",
- markup(long_content),
- )
- with open(os.path.join(settings.html_path, "verification.html"), "w") as f:
- f.write(make_html_forwarding_page("/verification/"))
-
- for i in verifications:
- if i == "symfem":
- continue
- if i.startswith("*(") and i.endswith(")"):
- continue
- write_html_page(
- os.path.join(settings.html_path, f"verification/{i}.html"),
- f"{implementations[i].name} verification",
- markup(impl_content[i]),
- extra_head=(
- ""
- ),
- )
-
- # Make example pages
- print("Making examples")
- if settings.processes == 1:
- for e in all_examples:
- build_example(e)
- else:
- import multiprocessing
-
- multiprocessing.set_start_method("fork")
-
- with multiprocessing.Pool(settings.processes) as p:
- p.map(build_example, all_examples)
-
- # Index page
- content = heading_with_self_ref("h1", "Index of elements")
- # Generate filtering Javascript
- content += ""
- content += "↓ Show filters ↓\n"
- content += "↑ Hide filters ↑\n"
- content += "
"
- content += (
- "
Alternative names
"
- " "
- " "
- " "
- "
"
- )
- content += "
Reference cells
"
- content += (
- " "
- for r in categoriser.references:
- content += f" "
- content += "
"
- content += "
Categories
"
- content += (
- " "
- for c in categoriser.categories:
- content += f" "
- content += "
"
- content += "
\n"
- # Write element list
- elementlist = []
- for e in categoriser.elements:
- id = " ".join(
- [f"ref-{r}" for r in e.reference_cells(False)]
- + [f"cat-{c}" for c in e.categories(False, False)]
- )
- elementlist.append(
- (
- e.html_name.lower(),
- (
- f"
"
-
- title = "Finite elements on a"
- if c[0] in "aeiou":
- title += "n"
- title += f" {c}"
- sub_content = heading_with_self_ref("h1", title)
- sub_content += "
" + "".join([i[1] for i in refels]) + "
"
-
- write_html_page(
- os.path.join(settings.htmlindices_path, f"references/{c}.html"),
- title,
- sub_content,
- )
-
- write_html_page(
- os.path.join(settings.htmlindices_path, "references/index.html"),
- "Reference cells",
- content,
- )
-
- # Families
- def linked_names(dim: str, fname: str, cell: str) -> str:
- """Create list of linked names.
-
- Args:
- dim: Dimension
- fname: Filename
- cell: Reference cell
-
- Returns:
- List of linked names
- """
- out = []
- for key, cname in keys_and_names:
- if key in data:
- out.append(
- f""
- "\\(" + cname(data[key], cell=cell, dim=dim) + "\\)"
- ""
- )
- return ", ".join(out)
-
- def de_rham_row(
- family: dict[str, dict[str, list[str]]],
- fname: str,
- cell: str,
- diff_form_deg: list[str],
- ) -> str:
- """Create HTML string of FEs.
-
- Args:
- family: Elements
- fname: Filename
- cell: Reference cell
- diff_form_deg: List of differential form degrees
-
- Returns:
- HTML string of FEs
- """
- row = ""
- if all(i in family[cell] for i in diff_form_deg):
- row += "
"
- return row
-
- de_rham_3d: list[str] = []
- de_rham_2d_hdiv: list[str] = []
- de_rham_2d_hcurl: list[str] = []
-
- for fname, data in categoriser.families["de-rham"].items():
- family = data["elements"]
- cnames = []
- for key, cname in keys_and_names:
- if key in data:
- cnames.append("\\(" + cname(data[key], dim="3") + "\\)")
- if len(cnames) == 0:
- raise ValueError(f"No name found for family: {fname}")
- sub_content = heading_with_self_ref("h1", "The " + " or ".join(cnames) + " family")
-
- assert len([i for i in ["simplex", "tp"] if i in family]) == 1
-
- sub_content += "
"
- for cell in ["simplex", "tp"]:
- if cell in family:
- for o in ["0", "1", "d-1", "d"]:
- if o in family[cell]:
- sub_content += f"
"
- content += heading_with_self_ref("h2", "De Rham complex in 2D")
- content += "
In 2D, \\(\\textbf{H}(\\text{div})\\) and \\(\\textbf{H}(\\text{curl})\\) "
- content += "are isomorphic via a 90 degree rotation \\(R\\). This means that we can define "
- content += "the de Rham complex in two ways.
"
- content += "
One variant uses \\(\\textbf{H}(\\text{div})\\) and the vector-valued "
- content += "\\(\\textbf{curl}\\) operator, "
- content += "\\(\\textbf{curl} \\, u = (\\partial_y u, -\\partial_x u)\\). "
- content += "Note that \\(\\textbf{curl} \\, u = R \\, \\nabla u\\).
These two de Rham complex definitions in 2D account for the "
- content += "double element diagrams in the orange boxes in the "
- content += ""
- content += "Periodic table of the finite elements. That is also why the "
- content += "boxes are orange, and not red nor yellow as in 3D.
"
- write_html_page(
- os.path.join(settings.htmlfamilies_path, "index.html"), "Complex families", content
- )
-
- # List of lists
- content = heading_with_self_ref("h1", "Lists of elements")
- content += "
"
- return out
-
- content = heading_with_self_ref("h1", "List of all pages") + list_pages("")
- with open(os.path.join(settings.html_path, "sitemap.html"), "w") as f:
- f.write(make_html_page(content))
-
- end_all = datetime.now(tz=timezone(timedelta()))
- print(f"Total time: {(end_all - start_all).total_seconds():.2f}s")
diff --git a/data/categories b/data/categories
deleted file mode 100644
index 2e7f6c99a0..0000000000
--- a/data/categories
+++ /dev/null
@@ -1,7 +0,0 @@
-scalar: Scalar-valued elements
-vector: Vector-valued elements
-matrix: Matrix-valued elements
-Hcurl: H(curl) conforming elements
-Hdiv: H(div) conforming elements
-mixed: Mixed elements
-macro: Macro elements
diff --git a/data/contributors b/data/contributors
deleted file mode 100644
index 0f286cdf1f..0000000000
--- a/data/contributors
+++ /dev/null
@@ -1,39 +0,0 @@
-- name: Scroggs, Matthew W.
- img: m-scroggs.jpg
- bluesky: mscroggs.co.uk
- mastodon: mscroggs@mathstodon.xyz
- github: mscroggs
- website: https://mscroggs.co.uk
- email: defelement@mscroggs.co.uk
- desc: "Matthew is a Research Software Engineer at the University College London who works on finite and boundary element methods. He is a developer of the open-source finite element software [FEniCSx](https://github.com/FEniCS) and the open-source boundary element software [Bempp](https://github.com/bempp)."
-
-- name: Dokken, Jørgen S.
- img: dokken.jpg
- github: jorgensd
- website: https://jsdokken.com
- email: dokken@simula.no
- desc: "Jørgen is a Senior Research Engineer at Simula Research Laboratory working on development and applications of the finite element method. He is a developer of the open-source finite element software [FEniCSx](https://github.com/FEniCS) and the author of the [DOLFINx tutorial](https://jsdokken.com/dolfinx-tutorial)."
-
-- name: Dean, Joseph P.
- img: dean.png
- github: jpdean
- email: jpd62@cam.ac.uk
- desc: "Joe is a postdoctoral research associate at the University of Cambridge. He works on finite element discretisations of multiphysics problems and is a developer of the [FEniCSx](https://github.com/FEniCS) finite element library."
-
-- name: Marsden, India
- img: marsden.jpeg
- github: indiamai
- email: marsden@maths.ox.ac.uk
- desc: "India is a DPhil Student at the University of Oxford. She works on the fundamentals of the finite element method and is a developer of [Firedrake](https://firedrakeproject.org), a finite element package."
-
-- name: Brubeck, Pablo D.
- img: brubeck.jpeg
- github: pbrubeck
- email: brubeck@protonmail.com
- desc: "Pablo is a postdoctoral research associate at the University of Oxford. He works on fast solvers for high-order finite element discretisations of PDEs and is a developer of [Firedrake](https://firedrakeproject.org), a finite element package."
-
-- name: Nobre, Nuno
- img: nobre.jpeg
- github: nmnobre
- email: nuno.nobre@stfc.ac.uk
- desc: "Nuno is a computational scientist at the STFC Hartree Centre. He collaborates with research institutions worldwide to optimise scientific applications for tomorrow's computer architectures. He has contributed to the [libMesh](https://github.com/libMesh/libmesh), [MOOSE](https://github.com/idaholab/moose) and [MFEM](https://github.com/mfem/mfem) finite element packages."
diff --git a/data/editors b/data/editors
deleted file mode 100644
index 382a5c93f3..0000000000
--- a/data/editors
+++ /dev/null
@@ -1,7 +0,0 @@
-# Github usernames of editors.
-# Editors will be sorted alphabetically when website is generated, so no need for any specific order here.
-- mscroggs
-- jorgensd
-- jpdean
-- pbrubeck
-- indiamai
diff --git a/data/families b/data/families
deleted file mode 100644
index 618c24d51e..0000000000
--- a/data/families
+++ /dev/null
@@ -1,35 +0,0 @@
-de-rham:
- P:
- arnold-logg: P
- cockburn-fu: 1
- P-:
- arnold-logg: P-
- cockburn-fu: 2
- Q-:
- arnold-logg: Q-
- cockburn-fu: 4
- S:
- arnold-logg: S
- cockburn-fu: 1
- S-:
- arnold-logg: S-
- cockburn-fu: 2
- TNT:
- cockburn-fu: 3
- CH:
- name:
- general: \mathrm{C}\mathrm{P}^{3-r}\mathrm{\Lambda}^r(\mathcal{R})
- '0': \mathrm{C}^1\mathrm{P}^3\mathrm{\Lambda}^0(\mathcal{R})
- d-1: \mathrm{C}_d^0\mathrm{P}^2\mathrm{\Lambda}^1(\mathcal{R})
- d: \mathrm{C}^0\mathrm{P}^1\mathrm{\Lambda}^2(\mathcal{R})
- references:
- - title: Generalized Finite Element Systems for smooth differential forms and Stokes' problem
- author:
- - Christansen, Snorre H.
- - Hu, Kaibo
- journal: Numerische Mathematik
- volume: 140
- year: 2018
- pagestart: 327
- pageend: 371
- doi: 10.1007/s00211-018-0970-6
diff --git a/data/polysets b/data/polysets
deleted file mode 100644
index 85dd805158..0000000000
--- a/data/polysets
+++ /dev/null
@@ -1,18 +0,0 @@
-poly:
- - \mathcal{P}
- - \operatorname{span}\left\{\prod_{i=1}^dx_i^{p_i}\middle|\sum_{i=1}^dp_i\leqslant k\right\}
-tpoly:
- - \hat{\mathcal{P}}
- - \operatorname{span}\left\{\prod_{i=1}^dx_i^{p_i}\middle|\sum_{i=1}^dp_i=k\right\}=\mathcal{P}_k\setminus\mathcal{P}_{k-1}
-qoly:
- - \mathcal{Q}
- - \operatorname{span}\left\{\prod_{i=1}^dx_i^{p_i}\middle|\max_i(p_i)\leqslant k\right\}
-tqoly:
- - \hat{\mathcal{Q}}
- - \operatorname{span}\left\{\prod_{i=1}^dx_i^{p_i}\middle|\max_i(p_i)=k\right\}=\mathcal{Q}_k\setminus\mathcal{Q}_{k-1}
-serendipity:
- - \mathcal{X}
- - \operatorname{span}\left\{\prod_{i=1}^dx_i^{p_i}\middle|k<\sum_{i=1}^da_i\leqslant k+\#\left\{i\in\{1,\dots,d\}\middle| a_i=1\right\}\right\}
-apoly:
- - \mathcal{A}
- - \operatorname{span}\bigcup_{p\in\mathcal{P}_k(\mathbb{R}^{d-1})}\left\{\left(\begin{array}{c}0\\xzp(y,z)\\-xyp(y,z)\end{array}\right),\left(\begin{array}{c}yzp(x,z)\\0\\-xyp(x,z)\end{array}\right),\left(\begin{array}{c}yzp(x,y)\\-xzp(x,y)\\0\end{array}\right)\right\}
diff --git a/data/references b/data/references
deleted file mode 100644
index 2d0b9d1168..0000000000
--- a/data/references
+++ /dev/null
@@ -1,9 +0,0 @@
-interval
-triangle
-tetrahedron
-quadrilateral
-tetrahedron
-hexahedron
-prism
-pyramid
-dual polygon
diff --git a/defelement/__init__.py b/defelement/__init__.py
deleted file mode 100644
index f41588cde3..0000000000
--- a/defelement/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-"""DefElement."""
diff --git a/defelement/caching.py b/defelement/caching.py
deleted file mode 100644
index ca7c7997db..0000000000
--- a/defelement/caching.py
+++ /dev/null
@@ -1,62 +0,0 @@
-"""Caching."""
-
-import json
-import os
-
-import symfem
-from webtools.tools import join
-
-from defelement import settings
-
-cache_version = "1.0.0"
-
-
-def load_cache(
- item_key: str,
- last_updated: str,
-) -> str | None:
- """Load item from cache."""
- if not settings.caching:
- return None
- try:
- with open(join(settings.cache_path, f"{item_key}.json")) as f:
- data = json.load(f)
- if data.get("symfem_version") != symfem.__version__:
- return None
- if data.get("last_updated") != last_updated:
- return None
- if data.get("cache_version") != cache_version:
- return None
- return data.get("content")
- except FileNotFoundError:
- return None
-
-
-def save_cache(item_key: str, last_updated: str, item: str):
- """Save item to cache."""
- if not settings.caching:
- return
- if not os.path.isdir(settings.cache_path):
- os.mkdir(settings.cache_path)
- data = {
- "content": item,
- "symfem_version": symfem.__version__,
- "last_updated": last_updated,
- "cache_version": cache_version,
- }
- with open(join(settings.cache_path, f"{item_key}.json"), "w") as f:
- return json.dump(data, f)
-
-
-def tidy_cache():
- """Remove old items from cache."""
- if not settings.caching or not os.path.isdir(settings.cache_path):
- return
- for file in os.listdir(settings.cache_path):
- with open(join(settings.cache_path, file)) as f:
- data = json.load(f)
- if (
- data.get("symfem_version") != symfem.__version__
- or data.get("cache_version") != cache_version
- ):
- os.remove(join(settings.cache_path, file))
diff --git a/defelement/citations.py b/defelement/citations.py
deleted file mode 100644
index d16ca923e1..0000000000
--- a/defelement/citations.py
+++ /dev/null
@@ -1,43 +0,0 @@
-"""Major references that are used in many places."""
-
-arnold_logg = {
- "title": "Periodic table of the finite elements",
- "author": "Arnold, Douglas N. and Logg, Anders",
- "journal": "SIAM News",
- "year": "2014",
- "volume": "47",
- "number": "9",
- "url": "https://www.siam.org/publications/siam-news/issues/volume-47-number-09-november-2014/",
-}
-
-cockburn_fu = {
- "title": "A systematic construction of finite element commuting exact sequences",
- "author": "Cockburn, Bernardo and Fu, Guosheng",
- "journal": "SIAM journal on numerical analysis",
- "volume": "55",
- "number": "4",
- "pagestart": "1650",
- "pageend": "1688",
- "year": "2017",
- "doi": "10.1137/16M1073352",
-}
-
-kirby_mapping = {
- "author": "Kirby, Robert C.",
- "title": "A general approach to transforming finite elements",
- "journal": "The SMAI journal of computational mathematics",
- "pagestart": "197",
- "pageend": "224",
- "volume": "4",
- "year": "2018",
- "doi": "10.5802/smai-jcm.33",
-}
-
-defelement_paper = {
- "type": "unpublished",
- "author": "Scroggs, Matthew W. and Brubeck, Pablo D. and Dean, Joseph P. and Dokken, Jørgen S. and Marsden, India",
- "title": "DefElement: an encyclopedia of finite element definitions",
- "year": "2025",
- "howpublished": "submitted to Computational Science and Engineering",
- "doi": "10.48550/arXiv.2506.20188",
-}
diff --git a/defelement/code_examples.py b/defelement/code_examples.py
deleted file mode 100644
index 4307f0b639..0000000000
--- a/defelement/code_examples.py
+++ /dev/null
@@ -1,87 +0,0 @@
-"""Generating code snippets."""
-
-import symfem
-
-from defelement.caching import load_cache, save_cache
-from defelement.element import Element
-from defelement.implementations import Implementation
-from defelement.languages import languages
-from defelement.tools import jsify
-
-
-def generate_examples(e: Element, impl: type[Implementation], language: str) -> str | None:
- """Generate code snippets.
-
- Args:
- e: The element to generate examples for
- impl: The implementation to generate example for
- language: Programming language
-
- Returns:
- Code snippets or None if none were generated
- """
- langname = languages[language].name
- assert impl.id is not None
- jscodename = jsify(impl.id)
- if not e.implemented(impl.id):
- return None
-
- # Implementations generated from other implementations (eg Basix code generated by Symfem)
- if impl.id.startswith("*(") and impl.id.endswith(")"):
- cache_id = f"{e.name}-{impl.id}-implementation-code"
- c = load_cache(cache_id, symfem.__version__)
- _input_code, output_code = impl.id[2:-1].split(" -> ")
- jscodename = jsify(output_code)
- if c is None:
- if e.implemented(output_code):
- return None
- try:
- example_code = e.make_implementation_examples(impl.id, language)
- save_cache(cache_id, symfem.__version__, example_code)
- except (NotImplementedError, KeyError):
- save_cache(cache_id, symfem.__version__, "_NONE")
- return None
- elif c == "_NONE":
- return None
- else:
- example_code = c
-
- # Standard implementations
- elif e.has_implementation_examples(impl.id):
- example_code = e.make_implementation_examples(impl.id, language)
-
- # Make the HTML for the examples
- info = (
- f""
- f"↓ Show {impl.name} {langname} examples ↓"
- f""
- f"↑ Hide {impl.name} {langname} examples ↑"
- f"
"
- )
-
- info += languages[language].install(impl)
-
- info += (
- "This element can then be created with the following lines of "
- f"{languages[language].name}:"
- f"
".join(
- [f"\\({i}\\): {make_dof_data(j)}" for a in ndofs for i, j in a.items()]
- )
-
- dof_text = []
- for i, j in ndofs.items():
- txt = f"{i}: "
- txt += make_formula(j)
- dof_text.append(txt)
-
- return " ".join(dof_text)
-
-
-def make_formula(data: dict[str, typing.Any]) -> str:
- """Make formula.
-
- Args:
- data: Element data
-
- Returns:
- Formula for number of DOFs in HTML format
- """
- txt = ""
- if "formula" not in data and "oeis" not in data:
- return ", ".join(f"{make_formula(j)} ({i})" for i, j in data.items())
- if "formula" in data:
- txt += "\\("
- if isinstance(data["formula"], list):
- txt += "\\begin{cases}"
- txt += "\\\\".join([f"{c}&{b}" for a in data["formula"] for b, c in a.items()])
- txt += "\\end{cases}"
- else:
- txt += f"{data['formula']}"
- txt += "\\)"
- if "oeis" in data:
- if "formula" in data:
- txt += " ("
- txt += f"{data['oeis']}"
- if "formula" in data:
- txt += ")"
- return txt
-
-
-def extract_degreemap(info):
- """Extract degree map information."""
- if isinstance(info, str):
- return info.split("DEGREEMAP=")[1].split()[0]
-
- assert isinstance(info, dict)
- maps = {i: extract_degreemap(j) for i, j in info.items() if i != "display"}
-
- value = next(iter(maps.values()))
- for i in maps.values():
- if i != value:
- return maps
- return value
-
-
-def degreemap_equal(a, b) -> bool:
- """Check if two degreemaps are equal."""
- if isinstance(a, str):
- return a == b
- assert isinstance(a, dict)
- for i in a:
- if not degreemap_equal(a[i], b[i]):
- return False
- return True
-
-
-class Element:
- """An element."""
-
- def __init__(self, data: dict[str, typing.Any], fname: str):
- """Initialise.
-
- Args:
- data: element data
- fname: filename
- """
- self.data = data
- self.filename = fname
- self._c: Categoriser | None = None
- self.created = None
- self.modified = None
-
- def name_with_variant(self, variant: str | None) -> str:
- """Get name with variant.
-
- Args:
- variant: The variant
-
- Return:
- Name with variant
- """
- if variant is None:
- return self.name
- return f"{self.name} ({self.variant_name(variant)} variant)"
-
- def variant_name(self, variant: str) -> str:
- """Get variant name.
-
- Args:
- variant: The variant
-
- Returns:
- The variant name
- """
- return self.data["variants"][variant]["variant-name"]
-
- def variants(self) -> list[str]:
- """Get variants of the element.
-
- Returns:
- A list of variants
- """
- if "variants" not in self.data:
- return []
- return [f"{v['variant-name']}: {v['description']}" for v in self.data["variants"].values()]
-
- def min_degree(self, ref: str) -> int:
- """Get the minimum degree.
-
- Args:
- ref: Reference cell
-
- Returns:
- The minimum degree
- """
- if "min-degree" not in self.data:
- return 0
- if isinstance(self.data["min-degree"], dict):
- return self.data["min-degree"][ref]
- return self.data["min-degree"]
-
- def max_degree(self, ref: str) -> int | None:
- """Get the maximum degree.
-
- Args:
- ref: Reference cell
-
- Returns:
- The maximum degree
- """
- if "max-degree" not in self.data:
- return None
- if isinstance(self.data["max-degree"], dict):
- return self.data["max-degree"][ref]
- return self.data["max-degree"]
-
- def degree_convention(self) -> str | None:
- """Get the degree convention.
-
- Returns:
- The degree convention
- """
- if "degree" not in self.data:
- return None
- return self.data["degree"]
-
- def _edegree(self, dtype: str) -> str | None:
- """Get an embedded degree.
-
- Returns:
- Information about the embedded degree
- """
- if dtype not in self.data:
- return None
-
- def to_tex(txt):
- if isinstance(txt, dict):
- return ", ".join(
- [
- to_tex(j) + " (" + ("otherwise" if i == "_" else f"degree={i}") + ")"
- for i, j in txt.items()
- ]
- )
- txt = str(txt)
- if txt == "none":
- return "undefined"
- txt = txt.replace("floor", "\\operatorname{floor}")
- txt = txt.replace("min", "\\min")
- txt = txt.replace("max", "\\max")
- return f"\\({txt}\\)"
-
- d = self.data[dtype]
- if isinstance(d, dict):
- return " ".join([f"{cell}: {to_tex(deg)}" for cell, deg in d.items()])
- return to_tex(d)
-
- def polynomial_subdegree(self) -> str | None:
- """Get the polynomial subdegree.
-
- Returns:
- The degree convention
- """
- return self._edegree("polynomial-subdegree")
-
- def polynomial_superdegree(self) -> str | None:
- """Get the polynomial superdegree.
-
- Returns:
- The degree convention
- """
- return self._edegree("polynomial-superdegree")
-
- def lagrange_subdegree(self) -> str | None:
- """Get the Lagrange subdegree.
-
- Returns:
- The degree convention
- """
- return self._edegree("lagrange-subdegree")
-
- def lagrange_superdegree(self) -> str | None:
- """Get the polynomial superdegree.
-
- Returns:
- The degree convention
- """
- return self._edegree("lagrange-superdegree")
-
- def reference_cells(self, link: bool = True) -> list[str]:
- """Get reference cells.
-
- Args:
- link: Should a link be included?
-
- Returns:
- List of reference cells.
- """
- if link:
- return [
- f"{e}"
- for e in self.data["reference-cells"]
- ]
- else:
- return self.data["reference-cells"]
-
- def alternative_names(
- self,
- include_bracketed: bool = True,
- include_complexes: bool = True,
- include_variants: bool = True,
- link: bool = True,
- strip_cell_name: bool = False,
- cell: str | None = None,
- ) -> list[str]:
- """Get alternative names.
-
- Args:
- include_bracketed: Should bracketed names be included?
- include_complexes: Should names complexes be included?
- include_variants: Should variants be included?
- link: Should the names be linked?
- strip_cell_names: Should cell names be stripped?
- cell: Reference cell
-
- Returns:
- A list of names
- """
- if "alt-names" not in self.data:
- return []
- out = self.data["alt-names"]
- if include_complexes:
- out += self.complexes(link=link)
- if include_variants and "variants" in self.data:
- for v in self.data["variants"].values():
- if "names" in v:
- out += [f"{i} ({v['variant-name']} variant)" for i in v["names"]]
-
- if include_bracketed:
- out = [i[1:-1] if i[0] == "(" and i[-1] == ")" else i for i in out]
- else:
- out = [i for i in out if i[0] != "(" or i[-1] != ")"]
-
- if cell is not None:
- out = [i for i in out if " (" not in i or cell in i]
-
- if strip_cell_name:
- out = [i.split(" (")[0] for i in out]
-
- return out
-
- def short_names(self, include_variants: bool = True) -> list[str]:
- """Get short names.
-
- Args:
- include_variants: Should variants be included?
-
- Returns:
- A list of short names
- """
- out = []
- if "short-names" in self.data:
- out += self.data["short-names"]
- if include_variants and "variants" in self.data:
- for v in self.data["variants"].values():
- if "short-names" in v:
- out += [f"{i} ({v['variant-name']} variant)" for i in v["short-names"]]
- return out
-
- def mapping(self) -> str | None:
- """Get mapping name.
-
- Returns:
- Mapping name
- """
- if "mapping" not in self.data:
- return None
- mapping = self.data["mapping"]
- while "{{citation::" in mapping:
- pre, post = mapping.split("{{citation::", 1)
- id, post = post.split("}}", 1)
- c = getattr(citations, id)
- if "references" not in self.data:
- self.data["references"] = []
- if c not in self.data["references"]:
- self.data["references"].append(c)
- index = self.data["references"].index(c) + 1
- mapping = f'{pre}[{index}]{post}'
- return mapping
-
- def sobolev(self) -> str | None:
- """Get Sobolev space name.
-
- Returns:
- Sobolev space
- """
- if "sobolev" not in self.data:
- return None
- return self.data["sobolev"]
-
- def complexes(self, link: bool = True, names: bool = True) -> dict[str, list[str]]:
- """Get complexes.
-
- Args:
- link: Should links be included?
- names: Should names be used?
-
- Returns:
- Complexes
- """
- assert self._c is not None
-
- if "complexes" not in self.data:
- return {}
-
- out: dict[str, list[str]] = {}
- com = self.data["complexes"]
- for key, families in com.items():
- out[key] = []
- if not isinstance(families, (list, tuple)):
- families = [families]
- for e in families:
- if names:
- namelist = []
- e_s = e.split(",")
- if len(e_s) == 3:
- fam, ext, cell = e_s
- k = "k"
- else:
- fam, ext, cell, k = e_s
- data = self._c.families[key][fam]
- for key2, f in keys_and_names:
- if key2 in data:
- namelist.append("\\(" + f(data[key2], ext, cell, k) + "\\)")
- entry = ""
- if link:
- entry = f""
- entry += " or ".join(namelist)
- if link:
- entry += ""
- out[key].append(entry)
- else:
- out[key].append(e)
- return out
-
- def degree_range(self) -> str:
- """Format the range of allowed degrees.
-
- Returns:
- The formatted range
- """
-
- def make_degree_data(
- min_o: dict[str, int] | int | None,
- max_o: dict[str, int] | int | None,
- ) -> str:
- """Make degree data.
-
- Args:
- min_o: The minimum degree
- max_o: The maximum degree
-
- Returns:
- The formatted degree
- """
- if isinstance(min_o, dict):
- degrees = []
- for i, min_i in min_o.items():
- if isinstance(max_o, dict) and i in max_o:
- degrees.append(i + ": " + make_degree_data(min_i, max_o[i]))
- else:
- degrees.append(i + ": " + make_degree_data(min_i, max_o))
- return " \n".join(degrees)
- if isinstance(max_o, dict):
- degrees = []
- for i, max_i in max_o.items():
- degrees.append(i + ": " + make_degree_data(min_o, max_i))
- return " \n".join(degrees)
- if max_o is None:
- return f"\\({min_o}\\leqslant k\\)"
- if max_o == min_o:
- return f"\\(k={min_o}\\)"
- return f"\\({min_o}\\leqslant k\\leqslant {max_o}\\)"
-
- return make_degree_data(
- self.data.get("min-degree", 0),
- self.data.get("max-degree", None),
- )
-
- def sub_elements(self, link: bool = True) -> list[str]:
- """Get sub elements of a mixed element.
-
- Args:
- link: Should a link be included?
-
- Returns:
- List of sub-elements
- """
- assert self.is_mixed
- assert self._c is not None
-
- out = []
- for e in self.data["mixed"]:
- element, degree = e.split("(")
- degree = degree.split(")")[0]
- space_link = self._c.get_space_name(element, link=link)
- out.append(f"
degree \\({degree}\\) {space_link} space
")
- return out
-
- def make_dof_descriptions(self) -> str:
- """Make DOF descroptions.
-
- Returns:
- Descriptions of DOFs
- """
- if "dofs" not in self.data:
- return ""
-
- def dofs_on_entity(entity: str, dofs: str | list[str]) -> str:
- """Get DOFs on an entity.
-
- Args:
- entity: The entity name
- dofs: The dofs
-
- Returns:
- Formatted DOFs
- """
- assert self._c is not None
- if not isinstance(dofs, str):
- doflist = [dofs_on_entity(entity, d) for d in dofs]
- if len(doflist) == 1:
- return doflist[0]
- return ", ".join(doflist[:-1]) + ", and " + doflist[-1]
- if "integral moment" in dofs:
- mom_type, space_info = dofs.split(" with ")
- space_info = space_info.strip()
- if space_info.startswith("{") and space_info.endswith("}"):
- return f"{mom_type} with \\(\\left\\{{{space_info[1:-1]}\\right\\}}\\)"
- if space_info.startswith('"') and space_info.endswith('"'):
- return f"{mom_type} with {insert_links(space_info[1:-1])}"
-
- def insert_space_links(matches):
- space_link = self._c.get_space_name(matches[1])
- return f"a degree \\({matches[2]}\\) {space_link} space"
-
- dofs = re.sub(r"\(([A-Za-z0-9\-]+),([^\)]*)\)", insert_space_links, dofs)
- return dofs
-
- def make_dof_d(data: dict[str, typing.Any], post: str = "") -> str:
- """Make a decription of a single DOF.
-
- Args:
- data: Data
- post: String to include after DOF name
-
- Returns:
- Formatted DOF
- """
- dof_data = []
- for i in [
- "interval",
- "triangle",
- "tetrahedron",
- "quadrilateral",
- "hexahedron",
- ]:
- if i in data:
- dof_data.append(make_dof_d(data[i], f" ({i})"))
- if len(dof_data) != 0:
- return " \n \n".join(dof_data)
-
- for i, j in [
- ("On each vertex", "vertices"),
- ("On each edge", "edges"),
- ("On each face", "faces"),
- ("On each volume", "volumes"),
- ("On each ridge", "ridges"),
- ("On each peak", "peaks"),
- ("On each facet", "facets"),
- ("On the interior of the reference cell", "cell"),
- ]:
- if j in data:
- if isinstance(data[j], dict):
- for shape, sub_data in data[j].items():
- if i.startswith("On each"):
- dof_data.append(
- f"{i} (of a {shape}){post}: {dofs_on_entity(j, sub_data)}"
- )
- else:
- assert i == "On the interior of the reference cell"
- dof_data.append(
- f"On the interior of a reference {shape}{post}: {dofs_on_entity(j, sub_data)}"
- )
-
- else:
- dof_data.append(f"{i}{post}: {dofs_on_entity(j, data[j])}")
- return " \n".join(dof_data)
-
- return make_dof_d(self.data["dofs"])
-
- def make_polynomial_set_html(self) -> str:
- """Format polynomial set / finite dimensional space as HTML.
-
- Returns:
- Formatted polynomial set
- """
- # TODO: move some of this to polynomial file
- if "polynomial-set" not in self.data:
- return ""
- psets: dict[str, list[str]] = {}
- if isinstance(self.data["polynomial-set"], dict):
- for i, j in self.data["polynomial-set"].items():
- if j not in psets:
- psets[j] = []
- psets[j].append(i)
- else:
- assert isinstance(self.data["polynomial-set"], str)
- psets[self.data["polynomial-set"]] = self.data["reference-cells"]
- if (
- "reference-cells" in self.data
- and len(psets) == 1
- and len(next(iter(psets.values()))) == len(self.data["reference-cells"])
- ):
- out = f"\\({make_poly_set(next(iter(psets.keys())))}\\) "
- else:
- out = ""
- for i, j in psets.items():
- out += f"\\({make_poly_set(i)}\\) ({', '.join(j)}) \n"
- extra = make_extra_info(" && ".join(psets.keys()))
- if len(extra) > 0:
- out += ""
- out += "↓ Show set definitions ↓"
- out += ""
- out += "↑ Hide set definitions ↑"
- out += "
"
- out += extra
- out += "
"
- out += ""
- return out
-
- def dof_counts(self) -> str:
- """Get DOF counts.
-
- Returns:
- DOF counts
- """
- if "ndofs" not in self.data:
- return ""
- return make_dof_data(self.data["ndofs"])
-
- def entity_dof_counts(self) -> str:
- """Get entity DOF counts.
-
- Returns:
- Entity DOF counts
- """
- if "entity-ndofs" not in self.data:
- return ""
- return make_dof_data(self.data["entity-ndofs"])
-
- @property
- def name(self) -> str:
- """Get element name.
-
- Returns:
- The element name
- """
- return self.data["name"]
-
- @property
- def legacy_filenames(self) -> list[str]:
- """Get any filenames that this element previously used.
-
- Returns:
- A list of filenames
- """
- if "legacy-names" not in self.data:
- return []
- return self.data["legacy-names"]
-
- @property
- def notes(self) -> list[str]:
- """Get notes.
-
- Returns:
- notes
- """
- if "notes" not in self.data:
- return []
- return self.data["notes"]
-
- @property
- def html_name(self) -> str:
- """Get HTML name.
-
- Returns:
- The HTML name
- """
- if "html-name" in self.data:
- return self.data["html-name"]
- else:
- return self.data["name"]
-
- @property
- def html_filename(self) -> str:
- """Get HTML filename.
-
- Returns:
- The filename
- """
- return f"{self.filename}.html"
-
- @property
- def is_mixed(self) -> bool:
- """Check if element is mixed.
-
- Returns:
- True if mixed, otherwise False
- """
- return "mixed" in self.data
-
- @property
- def html_link(self) -> str:
- """Get link to element.
-
- Returns:
- Link to this element
- """
- return f"{self.html_name}"
-
- def implemented(self, lib: str, include_dependent_implementations: bool = False) -> bool:
- """Check if element in implemented in a library.
-
- Args:
- lib: The library
-
- Returns:
- True if implemented, otherwise False
- """
- from defelement.implementations import implementations
-
- if not implementations[lib].implemented(self):
- return False
- if "implementations" in self.data and lib in self.data["implementations"]:
- return True
-
- if lib.startswith("*(") and lib.endswith(")"):
- assert not include_dependent_implementations
- in_lib, _ = lib[2:-1].split(" -> ")
- return "implementations" in self.data and in_lib in self.data["implementations"]
- elif include_dependent_implementations:
- for i in implementations:
- if i.endswith(f"-> {lib})") and self.implemented(i):
- return True
-
- return False
-
- def get_implementation_string(
- self,
- lib: str,
- reference: str | None,
- degree: int | None,
- variant: str | None = None,
- any_variant: bool | None = False,
- ) -> tuple[str, int | None, dict[str, typing.Any]]:
- """Get implementation string.
-
- Args:
- lib: Library
- reference: Reference cell
- degree: Degree
- variant: Variant name
- any_variant: Allow any variant if requested variant not found
-
- Returns:
- Implementation string, degree and parameters to pass to implementation
- """
- from defelement.implementations import (
- DegreeNotImplemented,
- NotImplementedOnReference,
- VariantNotImplemented,
- )
-
- assert self.implemented(lib)
- if variant is None:
- data = self.data["implementations"][lib]
- else:
- if variant not in self.data["implementations"][lib] and any_variant:
- for v in self.data["implementations"][lib]:
- if reference in self.data["implementations"][lib][v]:
- variant = v
- break
- if variant not in self.data["implementations"][lib]:
- raise VariantNotImplemented()
- data = self.data["implementations"][lib][variant]
- if isinstance(data, dict):
- if reference not in data:
- raise NotImplementedOnReference()
- out = data[reference]
- else:
- out = data
- params = {}
- if "=" in out:
- sp = out.split("=")
- out = " ".join(sp[0].split(" ")[:-1])
- sp[-1] += " "
- for i, j in itertools.pairwise(sp):
- i = i.split(" ")[-1]
- j = " ".join(j.split(" ")[:-1])
- params[i] = j
-
- if "DEGREES" in params:
- if degree is not None:
- for d in params["DEGREES"].split(","):
- if ":" in d:
- start, end = [int(i) for i in d.split(":")]
- if start <= degree < end:
- break
- elif degree == int(d):
- break
- else:
- raise DegreeNotImplemented()
- del params["DEGREES"]
-
- input_deg = degree
- if "DEGREEMAP" in params:
- if degree is not None:
- input_deg = int(sympy.S(params["DEGREEMAP"]).subs(sympy.Symbol("k"), degree))
- del params["DEGREEMAP"]
-
- return out, input_deg, params
-
- def list_of_implementation_strings(
- self,
- lib: str,
- joiner: str | None = " ",
- include_dependent_implementations: bool = False,
- ) -> str | list[str]:
- """Get a list of implementation strings.
-
- Args:
- lib: The library
- joiner: HTML to put between strings, or None if a list is desired
-
- Returns:
- List of implemtation strings
- """
- from defelement.implementations import implementations
-
- if include_dependent_implementations:
- assert not lib.startswith("*(")
- imp_list: list[str] = []
- if self.implemented(lib):
- imp_list += self.list_of_implementation_strings(lib, None)
- for other_lib in implementations:
- if other_lib.endswith(f" -> {lib}") and self.implemented(other_lib):
- imp_list += self.list_of_implementation_strings(other_lib, None)
-
- else:
- assert self.implemented(lib)
-
- if lib.startswith("*(") and lib.endswith(")"):
- return "Uses custom element code"
-
- if "display" in self.data["implementations"][lib]:
- d = implementations[lib].format(self.data["implementations"][lib]["display"], {})
- return f"{d}"
- if "variants" in self.data:
- variants = self.data["variants"]
- else:
- variants = {None: {}}
-
- i_dict: dict[str, list[str]] = {}
- for v, vinfo in variants.items():
- if v is None:
- data = self.data["implementations"][lib]
- else:
- if v not in self.data["implementations"][lib]:
- continue
- data = self.data["implementations"][lib][v]
- if isinstance(data, str):
- istring, _, params = self.get_implementation_string(lib, None, None, v)
- s = implementations[lib].format(istring, params)
- if s not in i_dict:
- i_dict[s] = []
- if v is None:
- i_dict[s].append("")
- else:
- i_dict[s].append(vinfo["variant-name"])
- else:
- for i in data:
- istring, _, params = self.get_implementation_string(lib, i, None, v)
- s = implementations[lib].format(istring, params)
- if s not in i_dict:
- i_dict[s] = []
- if v is None:
- i_dict[s].append(i)
- else:
- i_dict[s].append(f"{i}, {vinfo['variant-name']}")
- if len(i_dict) == 1:
- return f"{next(iter(i_dict.keys()))}"
- imp_list = [
- f"{i}({'; '.join(j)})"
- for i, j in i_dict.items()
- ]
- if joiner is None:
- return imp_list
- else:
- return joiner.join(imp_list)
-
- def make_implementation_examples(self, lib: str, language: str) -> str:
- """Make implementation examples for a library.
-
- Args:
- lib: The library
-
- Returns:
- Examples
- """
- from defelement.implementations import implementations
-
- return implementations[lib].examples(self, language)
-
- def has_implementation_examples(self, lib: str) -> bool:
- """Check if element has implementation examples for a library.
-
- Args:
- lib: The library
-
- Returns:
- True if library has examples, otherwise False
- """
- from defelement.implementations import examples
-
- return lib in examples
-
- def implementation_notes(self, lib: str) -> list[str]:
- """Get implementation notes for a library.
-
- Args:
- lib: The library
-
- Returns:
- Implementation notes
- """
- from defelement.implementations import implementations
-
- notes = implementations[lib].notes(self)
-
- if "implementations" in self.data and lib in self.data["implementations"]:
- impl = self.data["implementations"][lib]
- if "DEGREEMAP" in f"{impl}":
- degreemap = extract_degreemap(impl)
- if degreemap == "None":
- pass
- else:
- for id, info in [
- ("polynomial-subdegree", "polynomial subdegree"),
- ("lagrange-superdegree", "Lagrange superdegree"),
- ("polynomial-superdegree", "polynomial superdegree"),
- ("lagrange-subdegree", "Lagrange subdegree"),
- ]:
- if id in self.data and degreemap_equal(degreemap, self.data[id]):
- notes.append(
- f"This element uses the {info} as the canonical degree "
- "of this element"
- )
- break
- else:
- notes.append(
- "This implementation uses an alternative value of "
- "degree for this element"
- )
- return notes
-
- def implementation_references(self, lib: str) -> list[dict[str, str]]:
- """Get implementation notes for a library.
-
- Args:
- lib: The library
-
- Returns:
- Implementation notes
- """
- from defelement.implementations import implementations
-
- return implementations[lib].references(self)
-
- def categories(self, link: bool = True, map_name: bool = True) -> list[str]:
- """Get categories.
-
- Args:
- link: Should links be included?
- map_name: Should names be mapped?
-
- Returns:
- Categories
- """
- assert self._c is not None
-
- if "categories" not in self.data:
- return []
- if map_name:
- cnames = {c: self._c.get_category_name(c) for c in self.data["categories"]}
- else:
- cnames = {c: c for c in self.data["categories"]}
- if link:
- return [
- f"{cnames[c]}"
- for c in self.data["categories"]
- ]
- else:
- return [f"{cnames[c]}" for c in self.data["categories"]]
-
- def references(self) -> list[str]:
- """Get reference cells.
-
- Returns:
- reference cells
- """
- from defelement.implementations import implementations
-
- assert self._c is not None
-
- references = self.data.get("references", [])
-
- if "complexes" in self.data:
- for key, families in self.data["complexes"].items():
- if not isinstance(families, (list, tuple)):
- families = [families]
- for e in families:
- e_s = e.split(",")
- if len(e_s) == 3:
- fam, _ext, _cell = e_s
- else:
- fam, _ext, _cell, _k = e_s
- data = self._c.families[key][fam]
- if "arnold-logg" in data and arnold_logg_citation not in references:
- references.append(arnold_logg_citation)
- if "cockburn-fu" in data and cockburn_fu_citation not in references:
- references.append(cockburn_fu_citation)
- if "references" in data:
- for r in references:
- if r not in references:
- references.append(r)
- for i in implementations:
- if self.implemented(i):
- for ref in self.implementation_references(i):
- if ref not in references:
- references.append(ref)
- return references
-
- @property
- def test(self) -> bool:
- """Check if element should be tested by default.
-
- Returns:
- True if it should be tested, otherwise False
- """
- return "test" in self.data
-
- @property
- def has_examples(self) -> bool:
- """Check if element has examples.
-
- Returns:
- True if it has examples, otherwise False
- """
- return "examples" in self.data
-
- @property
- def examples(self) -> list[str]:
- """Get exmaples.
-
- Returns:
- List of examples
- """
- if "examples" not in self.data:
- return []
- return self.data["examples"]
-
-
-class Categoriser:
- """Categoriser."""
-
- def __init__(self):
- """Initialise."""
- self.elements = []
- self.families = {}
- self.references = {}
- self.categories = {}
-
- def recently_added(self, n: int) -> list[Element]:
- """Get recently added elements.
-
- Args:
- n: Number of elements
-
- Returns:
- List of recently added elements
- """
- if self.elements[0].created is None:
- return self.elements[:n]
- return sorted(self.elements, key=lambda e: e.created)[: -n - 1 : -1]
-
- def recently_updated(self, n: int) -> list[Element]:
- """Get recently updated elements.
-
- Args:
- n: Number of elements
-
- Returns:
- List of recently updated elements
- """
- if self.elements[0].modified is None:
- return self.elements[:n]
- return sorted(self.elements, key=lambda e: e.modified)[: -n - 1 : -1]
-
- def load_categories(self, file: str):
- """Load categories from a file.
-
- Args:
- file: Filename
- """
- with open(file) as f:
- for line in f:
- if line.strip() != "":
- a, b = line.split(":", 1)
- self.add_category(a.strip(), b.strip(), f"{a.strip()}.html")
-
- def load_families(self, file: str):
- """Load families from a file.
-
- Args:
- file: Filename
- """
- with open(file) as f:
- self.families = yaml.load(f, Loader=yaml.FullLoader)
- for t in self.families:
- for i in self.families[t]:
- self.families[t][i]["elements"] = {}
-
- def load_references(self, file: str):
- """Load references from a file.
-
- Args:
- file: Filename
- """
- with open(file) as f:
- for line in f:
- if line.strip() != "":
- self.add_reference(line.strip(), f"{line.strip()}.html")
-
- def load_folder(self, folder: str):
- """Load elements from a folder.
-
- Args:
- folder: Folder name
- """
- for file in os.listdir(folder):
- if file.endswith(".def") and not file.startswith("."):
- with open(os.path.join(folder, file)) as f:
- data = yaml.load(f, Loader=yaml.FullLoader)
-
- fname = file[:-4]
-
- self.add_element(Element(data, fname))
-
- if settings.github_token is None:
- warnings.warn("Building without Github token. Timestamps will not be obtained.")
- else:
- g = Github(settings.github_token)
- repo = g.get_repo("DefElement/DefElement")
- for e in self.elements:
- commits = repo.get_commits(path=f"elements/{e.filename}.def")
- try:
- e.created = commits.get_page(-1)[-1].commit.committer.date
- e.modified = commits.get_page(0)[0].commit.committer.date
- except IndexError:
- e.created = datetime.now(tz=pytz.utc)
- e.modified = datetime.now(tz=pytz.utc)
-
- self.elements.sort(key=lambda x: x.name.lower())
-
- def add_family(self, t: str, e: str, name: str, fname: str):
- """Add a family.
-
- Args:
- t: Family name
- e: Element information
- name: Element name
- fname: Filename
- """
- if len(e.split(",")) == 3:
- i, j, k = e.split(",")
- else:
- i, j, k, _ = e.split(",")
- if t not in self.families:
- self.families[t] = {}
- warnings.warn(f"Complex type included in familes data: {t}")
- if i not in self.families[t]:
- warnings.warn(f"Family not included in familes data: {i}")
- self.families[t][i] = {"elements": {}}
- if k not in self.families[t][i]["elements"]:
- self.families[t][i]["elements"][k] = {}
- self.families[t][i]["elements"][k][j] = (name, fname)
-
- def add_reference(self, e: str, fname: str):
- """Add reference cell.
-
- Args:
- e: Reference name
- fname: filename
- """
- self.references[e] = fname
-
- def add_category(self, fname: str, cname: str, html_filename: str):
- """Add a category.
-
- Args:
- fname: Filename
- cname: Category name
- html_filename: HTML filename to link to
- """
- self.categories[fname] = (cname, html_filename)
-
- def get_category_name(self, c: str) -> str:
- """Get category name.
-
- Args:
- c: Category
-
- Returns:
- Category name
- """
- return self.categories[c][0]
-
- def get_space_name(self, element: str, link: bool = True) -> str:
- """Get element space name.
-
- Args:
- element: Element id
- link: Should a link be included?
-
- Returns:
- Space name, with or without a link to the element page
- """
- for e in self.elements:
- if e.filename == element:
- if link:
- return e.html_link
- else:
- return e.html_name
- break
- raise ValueError(f"Could not find space: {element}")
-
- def get_element(self, ename: str) -> Element:
- """Get an element.
-
- Args:
- ename: Element id
-
- Returns:
- The element
- """
- for e in self.elements:
- if e.name == ename:
- return e
- raise ValueError(f"Could not find element: {ename}")
-
- def add_element(self, e: Element):
- """Add an element.
-
- Args:
- e: The element
- """
- self.elements.append(e)
- e._c = self
- for r in e.reference_cells(False):
- assert r in self.references
-
- for j, k in e.complexes(False, False).items():
- for i in k:
- self.add_family(j, i, e.html_name, e.html_filename)
-
- def elements_in_category(self, c: str) -> list[Element]:
- """Get elements in a category.
-
- Args:
- c: Category id
-
- Returns:
- List of elements
- """
- return [e for e in self.elements if c in e.categories(False, False)]
-
- def elements_in_implementation(
- self, i: str, include_dependent_implementations=False
- ) -> list[Element]:
- """Get elements in an implementation.
-
- Args:
- i: Implementation
-
- Returns:
- List of elements
- """
- return [
- e
- for e in self.elements
- if e.implemented(i, include_dependent_implementations=include_dependent_implementations)
- ]
-
- def elements_by_reference(self, r: str) -> list[Element]:
- """Get elements on a reference.
-
- Args:
- r: Reference
-
- Returns:
- List of elements
- """
- return [e for e in self.elements if r in e.reference_cells(False)]
diff --git a/defelement/examples.py b/defelement/examples.py
deleted file mode 100644
index 831249ca88..0000000000
--- a/defelement/examples.py
+++ /dev/null
@@ -1,219 +0,0 @@
-"""Examples."""
-
-import os
-
-import sympy
-from symfem.finite_element import CiarletElement, DirectElement, FiniteElement
-from symfem.functionals import BaseFunctional
-from symfem.functions import Function
-from symfem.piecewise_functions import PiecewiseFunction
-from symfem.symbols import t
-from webtools.html import make_html_forwarding_page, make_html_page
-from webtools.markup import heading_with_self_ref
-
-from defelement import plotting, settings, symbols
-from defelement.caching import load_cache, save_cache
-
-defelement_t = ["s_{0}", "s_{1}", "s_{2}"]
-
-
-def to_tex(
- f: Function
- | sympy.core.expr.Expr
- | list[Function | sympy.core.expr.Expr]
- | tuple[Function | sympy.core.expr.Expr, ...],
- tfrac: bool = False,
-) -> str:
- """Convert function to TeX.
-
- Args:
- f: A function
- tfrac: Should tfrac be used in the place of frac?
-
- Returns:
- TeX
- """
- if isinstance(f, PiecewiseFunction):
- sub_f = None
- for p in f.pieces.values():
- if sub_f is None:
- sub_f = p
- if p != sub_f:
- break
- else:
- return to_tex(sub_f)
-
- if isinstance(f, (list, tuple)):
- return (
- "\\left(\\begin{array}{c}"
- + "\\\\".join(["\\displaystyle " + to_tex(i) for i in f])
- + "\\end{array}\\right)"
- )
- elif isinstance(f, Function):
- out = f.as_tex()
- else:
- out = sympy.latex(sympy.simplify(sympy.expand(f)))
- out = out.replace("\\left[", "\\left(")
- out = out.replace("\\right]", "\\right)")
-
- for i, j in zip(t, defelement_t):
- out = out.replace(sympy.latex(i), j)
-
- if tfrac:
- return out.replace("\\frac", "\\tfrac")
- else:
- return out
-
-
-def entity_name(dim: int) -> str:
- """Get the name of a sub-entity.
-
- Args:
- dim: The dimension
-
- Returns:
- Sub-entity name
- """
- return ["vertex", "edge", "face", "volume"][dim]
-
-
-def describe_dof(element: FiniteElement, d: BaseFunctional) -> tuple[str, list[str]]:
- """Describe a DOF.
-
- Args:
- element: The element
- d: The DOF
-
- Returns:
- Formatted DOF, and list of symbols included in definition
- """
- desc, symb = d.get_tex()
-
- for i, j in zip(t, defelement_t):
- desc = desc.replace(sympy.latex(i), j)
-
- for j in defelement_t:
- if j in desc:
- dim = element.reference.tdim
- new_s = "\\("
- if dim == 1:
- new_s += defelement_t[0]
- else:
- new_s += ",".join(defelement_t[:dim])
- new_s += f"\\) is a parametrisation of \\({d.entity_tex()}\\)"
- symb.append(new_s)
- break
-
- return desc, symb
-
-
-def markup_example(
- element: FiniteElement,
- html_name: str,
- element_page: str,
- fname: str,
- legacy_filenames: list[str] | None = None,
-) -> str:
- """Markup examples.
-
- Args:
- element: The element
- html_name: Name of element
- element_page: URL of elemtn page
- fname: Filename
- legacy_filenames: Old filenames to create redirects from
-
- Returns:
- Example as HTML
- """
- if legacy_filenames is None:
- legacy_filenames = []
- eg = heading_with_self_ref(
- "h1", f"Degree {element.order} {html_name} on a {element.reference.name}"
- )
- eg += "\n"
- eg += f"◀ Back to {html_name} definition page"
- eg += "\n"
- eg += "
" + plotting.plot_dof_diagram(element) + "
\n"
- eg += "In this example:\n
\n"
- # Reference
- eg += f"
\\({symbols.reference}\\) is the reference {element.reference.name}."
- eg += " The following numbering of the sub-entities of the reference cell is used:
"
- basis += "This DOF is associated with "
- basis += entity_name(dof.entity[0]) + f" {dof.entity[1]}"
- basis += " of the reference cell."
- elif isinstance(element, DirectElement):
- basis += "
"
- basis += "This DOF is associated with "
- basis += entity_name(element._basis_entities[dof_i][0])
- basis += f" {element._basis_entities[dof_i][1]}"
- basis += " of the reference cell."
- basis += "
"
- basis += "
"
- save_cache(cache_key, element.last_updated, basis)
-
- eg += basis
-
- with open(os.path.join(os.path.join(settings.htmlelement_path, "examples", fname)), "w") as f:
- f.write(make_html_page(eg))
-
- for i in legacy_filenames:
- with open(os.path.join(os.path.join(settings.htmlelement_path, "examples", i)), "w") as f:
- f.write(make_html_forwarding_page(f"/elements/examples/{fname}"))
-
- return f"/elements/examples/{fname}"
diff --git a/defelement/families.py b/defelement/families.py
deleted file mode 100644
index 34fd66296d..0000000000
--- a/defelement/families.py
+++ /dev/null
@@ -1,108 +0,0 @@
-"""Element families."""
-
-
-def arnold_logg_name(
- family: str,
- r: str = "r",
- cell: str | None = None,
- degree: str = "k",
- dim: str = "d",
-) -> str:
- """Get the name used in the Periodic Table of Finite Elements.
-
- Args:
- family: Family
- r: Exterior derivative order
- cell: Cell type
- degree: Polynomial degree
- dim: Cell dimension
-
- Returns:
- Formatted name
- """
- out = f"\\mathcal{{{family[0]}}}"
- if len(family) > 1:
- out += f"^{family[1]}"
- out += f"_{{{degree}}}"
- out += f"\\Lambda^{{{r}}}"
- if cell is not None:
- if cell == "simplex":
- out += f"(\\Delta_{dim})"
- elif cell == "tp":
- out += f"(\\square_{dim})"
- else:
- raise ValueError(f"Unknown cell: {cell}")
- return out
-
-
-def cockburn_fu_name(
- family: str,
- r: str = "r",
- cell: str | None = None,
- degree: str = "k",
- dim: str = "d",
-) -> str:
- """Get the name used in the Cockburn-Fu paper.
-
- Args:
- family: Family
- r: Exterior derivative order
- cell: Cell type
- degree: Polynomial degree
- dim: Cell dimension
-
- Returns:
- Formatted name
- """
- out = ""
- if r != "r":
- out += "\\left["
- out += f"S_{{{family},{degree}}}"
- if cell is not None:
- if cell == "simplex":
- out += "^\\unicode{0x25FA}"
- elif cell == "tp":
- out += "^\\square"
- else:
- raise ValueError(f"Unknown cell: {cell}")
- if r != "r":
- out += f"\\right]_{{{r}}}"
- return out
-
-
-def custom_name(
- family: str,
- r: str = "r",
- cell: str | None = None,
- degree: str = "k",
- dim: str = "d",
-):
- """Get a custom name.
-
- Args:
- family: Family
- r: Exterior derivative order
- cell: Cell type
- degree: Polynomial degree
- dim: Cell dimension
-
- Returns:
- Formatted name
- """
- out = family
- if isinstance(family, dict):
- if r == "r":
- out = family["general"]
- else:
- out = family[r]
- out = out.replace("", f"{r}")
- out = out.replace("", f"{dim}")
- out = out.replace("", f"{degree}")
- return out
-
-
-keys_and_names = [
- ("cockburn-fu", cockburn_fu_name),
- ("arnold-logg", arnold_logg_name),
- ("name", custom_name),
-]
diff --git a/defelement/implementations/__init__.py b/defelement/implementations/__init__.py
deleted file mode 100644
index 88778cbdba..0000000000
--- a/defelement/implementations/__init__.py
+++ /dev/null
@@ -1,41 +0,0 @@
-"""Implementations."""
-
-import importlib
-import os
-from inspect import isclass
-
-from defelement.implementations.core import (
- DegreeNotImplemented,
- Implementation,
- NotImplementedOnReference,
- VariantNotImplemented,
- parse_example,
-)
-
-__all__ = [
- "DegreeNotImplemented",
- "Implementation",
- "NotImplementedOnReference",
- "VariantNotImplemented",
- "examples",
- "formats",
- "implementations",
- "parse_example",
- "verifications",
-]
-
-implementations = {}
-this_dir = os.path.dirname(os.path.realpath(__file__))
-for file in os.listdir(this_dir):
- if file.endswith(".py") and not file.startswith("_") and file != "core.py":
- mod = importlib.import_module(f"defelement.implementations.{file[:-3]}")
- for name in dir(mod):
- if not name.startswith("_"):
- c = getattr(mod, name)
- if isclass(c) and c != Implementation and issubclass(c, Implementation):
- implementations[c.id] = c
-
-formats = {id: i.format for id, i in implementations.items()}
-examples = {id: i.examples for id, i in implementations.items()}
-versions = {id: i.version for id, i in implementations.items()}
-verifications = {id: i.verify for id, i in implementations.items() if i.verification}
diff --git a/defelement/implementations/basix.py b/defelement/implementations/basix.py
deleted file mode 100644
index f26ebb10da..0000000000
--- a/defelement/implementations/basix.py
+++ /dev/null
@@ -1,122 +0,0 @@
-"""Basix implementation."""
-
-import typing
-
-from numpy import float64
-from numpy.typing import NDArray
-
-from defelement.element import Element
-
-#
-from defelement.implementations.core import Implementation, pypi_name
-
-
-@pypi_name("fenics-basix")
-class BasixImplementation(Implementation):
- """Basix implementation."""
-
- #
-
- @classmethod
- def format(cls, string: str, params: dict[str, typing.Any]) -> str:
- """Format implementation string."""
- out = f"basix.ElementFamily.{string}"
- for p, v in params.items():
- out += f", {p}="
- if p == "lagrange_variant":
- out += f"basix.LagrangeVariant.{v}"
- elif p == "dpc_variant":
- out += f"basix.DPCVariant.{v}"
- elif p == "discontinuous":
- out += v
- else:
- raise ValueError(f"Unexpected parameter: {p}")
- return out
-
- @classmethod
- def example_import(cls, language: str) -> str:
- """Get imports to include at start of example."""
- return "import basix"
-
- @classmethod
- def single_example(
- cls,
- name: str,
- reference: str,
- degree: int,
- params: dict[str, str],
- language: str,
- element: Element,
- example: str,
- ) -> str:
- """Generate examples."""
- out = "element = basix.create_element("
- out += f"basix.ElementFamily.{name}, basix.CellType.{reference}, {degree}"
- if "lagrange_variant" in params:
- out += f", lagrange_variant=basix.LagrangeVariant.{params['lagrange_variant']}"
- if "dpc_variant" in params:
- out += f", dpc_variant=basix.DPCVariant.{params['dpc_variant']}"
- if "discontinuous" in params:
- assert params["discontinuous"] in ["True", "False"]
- out += f", discontinuous={params['discontinuous']}"
- out += ")"
- return out
-
- @classmethod
- def verify(
- cls,
- name: str,
- reference: str,
- degree: int,
- params: dict[str, str],
- element: Element,
- example: str,
- ) -> tuple[list[list[list[int]]], typing.Callable[[NDArray[float64]], NDArray[float64]]]:
- """Get verification data."""
- import basix
-
- kwargs = {}
- if "lagrange_variant" in params:
- kwargs["lagrange_variant"] = getattr(basix.LagrangeVariant, params["lagrange_variant"])
- if "dpc_variant" in params:
- kwargs["dpc_variant"] = getattr(basix.DPCVariant, params["dpc_variant"])
- if "discontinuous" in params:
- kwargs["discontinuous"] = params["discontinuous"] == "True"
-
- e = basix.create_element(
- getattr(basix.ElementFamily, name),
- getattr(basix.CellType, reference),
- degree,
- **kwargs,
- )
-
- entity_dofs = e.entity_dofs
- if reference == "triangle":
- entity_dofs[1] = [entity_dofs[1][2], entity_dofs[1][1], entity_dofs[1][0]]
- elif reference == "tetrahedron":
- entity_dofs[1] = [
- entity_dofs[1][5],
- entity_dofs[1][4],
- entity_dofs[1][3],
- entity_dofs[1][2],
- entity_dofs[1][1],
- entity_dofs[1][0],
- ]
- entity_dofs[2] = [
- entity_dofs[2][3],
- entity_dofs[2][2],
- entity_dofs[2][1],
- entity_dofs[2][0],
- ]
-
- def tabulate(points):
- table = e.tabulate(0, points)[0]
- return table.transpose((0, 2, 1))
-
- return entity_dofs, tabulate
-
- id = "basix"
- name = "Basix"
- url = "https://github.com/FEniCS/basix"
- verification = True
- languages = ("python",)
diff --git a/defelement/implementations/basix_ufl.py b/defelement/implementations/basix_ufl.py
deleted file mode 100644
index ee34c02fe6..0000000000
--- a/defelement/implementations/basix_ufl.py
+++ /dev/null
@@ -1,295 +0,0 @@
-"""Basix.UFL implementation."""
-
-import typing
-
-from numpy import float64
-from numpy.typing import NDArray
-
-from defelement.element import Element
-from defelement.implementations.basix import BasixImplementation
-from defelement.implementations.core import (
- Implementation,
- parse_example,
- pypi_name,
-)
-
-
-@pypi_name("fenics-basix", ["fenics-ufl"])
-class BasixUFLImplementation(Implementation):
- """Basix.UFL implementation."""
-
- @classmethod
- def format(cls, string: str, params: dict[str, typing.Any]) -> str:
- """Format implementation string."""
- out = BasixImplementation.format(string, {i: j for i, j in params.items() if i != "shape"})
- if "shape" in params:
- out += f", shape={params['shape']}"
- return out
-
- @classmethod
- def example_import(cls, language: str) -> str:
- """Get imports to include at start of example."""
- return "import basix\nimport basix.ufl"
-
- @classmethod
- def single_example(
- cls,
- name: str,
- reference: str,
- degree: int,
- params: dict[str, str],
- language: str,
- element: Element,
- example: str,
- ) -> str:
- """Generate code for a single example."""
- out = "element = basix.ufl.element("
- out += f"basix.ElementFamily.{name}, basix.CellType.{reference}, {degree}"
- if "lagrange_variant" in params:
- out += f", lagrange_variant=basix.LagrangeVariant.{params['lagrange_variant']}"
- if "dpc_variant" in params:
- out += f", dpc_variant=basix.DPCVariant.{params['dpc_variant']}"
- if "discontinuous" in params:
- assert params["discontinuous"] in ["True", "False"]
- out += f", discontinuous={params['discontinuous']}"
- if "shape" in params:
- if reference == "interval":
- dim = 1
- elif reference in ["triangle", "quadrilateral"]:
- dim = 2
- else:
- dim = 3
- out += ", shape=" + params["shape"].replace("dim", f"{dim}")
- out += ")"
- return out
-
- @classmethod
- def verify(
- cls,
- name: str,
- reference: str,
- degree: int,
- params: dict[str, str],
- element: Element,
- example: str,
- ) -> tuple[list[list[list[int]]], typing.Callable[[NDArray[float64]], NDArray[float64]]]:
- """Get verification data."""
- import basix
- import basix.ufl
-
- kwargs = {}
- if "lagrange_variant" in params:
- kwargs["lagrange_variant"] = getattr(basix.LagrangeVariant, params["lagrange_variant"])
- if "dpc_variant" in params:
- kwargs["dpc_variant"] = getattr(basix.DPCVariant, params["dpc_variant"])
- if "discontinuous" in params:
- kwargs["discontinuous"] = params["discontinuous"] == "True"
- if "shape" in params:
- if reference == "interval":
- dim = 1
- elif reference in ["triangle", "quadrilateral"]:
- dim = 2
- else:
- dim = 3
- kwargs["shape"] = tuple(
- dim if i == "dim" else int(i) for i in params["shape"][1:-1].split(",") if i != ""
- )
-
- e = basix.ufl.element(
- getattr(basix.ElementFamily, name),
- getattr(basix.CellType, reference),
- degree,
- **kwargs,
- )
-
- entity_dofs = e.entity_dofs
- if reference == "triangle":
- entity_dofs[1] = [entity_dofs[1][2], entity_dofs[1][1], entity_dofs[1][0]]
- elif reference == "tetrahedron":
- entity_dofs[1] = [
- entity_dofs[1][5],
- entity_dofs[1][4],
- entity_dofs[1][3],
- entity_dofs[1][2],
- entity_dofs[1][1],
- entity_dofs[1][0],
- ]
- entity_dofs[2] = [
- entity_dofs[2][3],
- entity_dofs[2][2],
- entity_dofs[2][1],
- entity_dofs[2][0],
- ]
-
- def tabulate(points):
- table = e.tabulate(0, points)[0]
- return table.reshape(points.shape[0], e.reference_value_size, -1)
-
- return entity_dofs, tabulate
-
- id = "basix.ufl"
- name = "Basix.UFL"
- url = "https://github.com/FEniCS/basix"
- verification = True
- languages = ("python",)
-
-
-class CustomBasixUFLImplementation(BasixUFLImplementation):
- """Basix.UFL implementation via custom element."""
-
- @classmethod
- def format(cls, string: str, params: dict[str, typing.Any]) -> str:
- """Format implementation string."""
- raise NotImplementedError()
-
- @classmethod
- def example_import(cls, language: str) -> str:
- """Get imports to include at start of example."""
- return "import basix\nimport basix.ufl\nimport numpy as np"
-
- @classmethod
- def single_example(
- cls,
- name: str,
- reference: str,
- degree: int,
- params: dict[str, str],
- language: str,
- element: Element,
- example: str,
- ) -> str:
- """Generate code for a single example."""
- import symfem
- import symfem.basix_interface
-
- cell, degree, variant, kwargs = parse_example(example)
- symfem_name, symfem_degree, params = element.get_implementation_string(
- "symfem", cell, degree, variant
- )
- if "variant" in params:
- kwargs["variant"] = params["variant"]
- symfem_e = symfem.create_element(cell, symfem_name, symfem_degree, **kwargs) # type: ignore
-
- return symfem.basix_interface.generate_basix_element_code(
- symfem_e, include_comment=False, include_imports=False, ufl=True
- )
-
- @classmethod
- def verify(
- cls,
- name: str,
- reference: str,
- degree: int,
- params: dict[str, str],
- element: Element,
- example: str,
- ) -> tuple[list[list[list[int]]], typing.Callable[[NDArray[float64]], NDArray[float64]]]:
- """Get verification data."""
- import basix.ufl
- import symfem
- import symfem.basix_interface
-
- kwargs = {}
- if "variant" in params:
- kwargs["variant"] = params["variant"]
- symfem_e = symfem.create_element(reference, name, degree, **kwargs) # type: ignore
-
- e = symfem.basix_interface.create_basix_element(symfem_e, ufl=True)
- assert isinstance(e, basix.ufl._ElementBase)
-
- entity_dofs = e.entity_dofs
- if reference == "triangle":
- entity_dofs[1] = [entity_dofs[1][2], entity_dofs[1][1], entity_dofs[1][0]]
- elif reference == "tetrahedron":
- entity_dofs[1] = [
- entity_dofs[1][5],
- entity_dofs[1][4],
- entity_dofs[1][3],
- entity_dofs[1][2],
- entity_dofs[1][1],
- entity_dofs[1][0],
- ]
- entity_dofs[2] = [
- entity_dofs[2][3],
- entity_dofs[2][2],
- entity_dofs[2][1],
- entity_dofs[2][0],
- ]
-
- def tabulate(points):
- table = e.tabulate(0, points)[0]
- return table.reshape(points.shape[0], e.reference_value_size, -1)
-
- return entity_dofs, tabulate
-
- @classmethod
- def implemented(cls, element: Element) -> bool:
- """Check if an element is implemented."""
- # Elements with DOFs that include derivatives
- if element.filename in [
- "alfeld-sorokina",
- "argyris",
- "arnold-boffi-falk",
- "bell",
- "bernardi-raugel",
- "bogner-fox-schmitt",
- "hermite",
- "morley",
- "morley-wang-xu",
- "taylor",
- "wu-xu",
- ]:
- return False
-
- # D(div curl) elements
- if element.filename in [
- "gopalakrishnan-lederer-schoberl",
- ]:
- return False
-
- # Macro elements
- if element.filename in [
- "alfeld-sorokina",
- "guzman-neilan",
- "guzman-neilan2",
- "hsieh-clough-tocher",
- "johnson-mercier",
- "p1-iso-p2",
- "p1-macro",
- "reduced-hsieh-clough-tocher",
- ]:
- return False
-
- # Elements with different numbers of DOFs on entities of the same type
- if element.filename in [
- "fortin-soulie",
- "transition",
- ]:
- return False
-
- # Mixed elements
- if element.filename in [
- "mini",
- "pechstein-schoberl",
- "taylor-hood",
- "scott-vogelius",
- ]:
- return False
-
- # Dual elements
- if element.filename in [
- "buffa-christiansen",
- "dual",
- "rotated-buffa-christiansen",
- ]:
- return False
-
- # non-Ciarlet elements
- return element.filename not in [
- "direct-serendipity",
- "enriched-galerkin",
- "lfeg",
- "rotated-buffa-christiansen",
- ]
-
- id = "*(symfem -> basix.ufl)"
diff --git a/defelement/implementations/bempp_cl.py b/defelement/implementations/bempp_cl.py
deleted file mode 100644
index c0244aaa00..0000000000
--- a/defelement/implementations/bempp_cl.py
+++ /dev/null
@@ -1,40 +0,0 @@
-"""Bempp-cl implementation."""
-
-import typing
-
-from defelement.element import Element
-from defelement.implementations.core import Implementation, pypi_name
-
-
-@pypi_name("bempp-cl", ["numba", "scipy", "meshio"])
-class BemppClImplementation(Implementation):
- """Bempp-cl implementation."""
-
- @classmethod
- def format(cls, string: str, params: dict[str, typing.Any]) -> str:
- """Format implementation string."""
- return f'"{string}"'
-
- @classmethod
- def example_import(cls, language: str) -> str:
- """Get imports to include at start of example."""
- return "import bempp_cl.api\ngrid = bempp_cl.api.shapes.regular_sphere(1)"
-
- @classmethod
- def single_example(
- cls,
- name: str,
- reference: str,
- degree: int,
- params: dict[str, str],
- language: str,
- element: Element,
- example: str,
- ) -> str:
- """Generate code for a single example."""
- return f'element = bempp_cl.api.function_space(grid, "{name}", {degree})'
-
- id = "bempp-cl"
- name = "Bempp-cl"
- url = "https://github.com/bempp/bempp-cl"
- languages = ("python",)
diff --git a/defelement/implementations/core.py b/defelement/implementations/core.py
deleted file mode 100644
index a149bc0eea..0000000000
--- a/defelement/implementations/core.py
+++ /dev/null
@@ -1,358 +0,0 @@
-"""Implementation template class and other functions."""
-
-import re
-import typing
-
-from numpy import float64
-from numpy.typing import NDArray
-
-from defelement.element import Element
-
-
-def pypi_name(package_name: str, dependencies: list[str] | None = None):
- """Use the PyPI name of a package to define the install variable and version method.
-
- Args:
- package_name: The name of the package on PyPI
- dependencies: Packages to install before installing the package
-
- Returns:
- A wrapped class
- """
-
- def pypi_name(Cls):
- class Wrapped(Cls):
- @classmethod
- def version(cls) -> str:
- """Get the version number of this implementation."""
- from importlib.metadata import PackageNotFoundError, version
-
- try:
- return version(package_name)
- except PackageNotFoundError:
- import requests
-
- return requests.get(f"https://pypi.org/pypi/{package_name}/json").json()[
- "info"
- ]["version"]
-
- @classmethod
- def install(cls, language: str) -> str | None:
- """Get the command(s) to install this implementation."""
- if language == "python":
- return (
- ""
- if dependencies is None
- else "pip install " + " ".join(dependencies) + "\n"
- ) + f"pip install {package_name}"
- return None
-
- return Wrapped
-
- return pypi_name
-
-
-class Implementation:
- """An implementation."""
-
- @classmethod
- def format(cls, string: str, params: dict[str, typing.Any]) -> str:
- """Format implementation string.
-
- This function is passed the string and parameters set in a .def file for an
- implementation and should return the string to display on the element's page.
-
- This function must be implemented.
-
- Args:
- string: Implementation string as set in the .def file
- params: Additional parameters set in the .def file
-
- Returns:
- Formatted implementation string
- """
- raise NotImplementedError()
-
- @classmethod
- def example_import(cls, language: str) -> str:
- """Get code for imports to include at start of examples snippet.
-
- This function must be implemented.
-
- Args:
- language: Programming language
-
- Returns:
- Python code for imports
- """
- raise NotImplementedError()
-
- @classmethod
- def single_example(
- cls,
- name: str,
- reference: str,
- degree: int,
- params: dict[str, str],
- language: str,
- element: Element,
- example: str,
- ) -> str:
- """Generate code for a single example.
-
- This function takes the element string and parameters (as set in the .def file)
- and the reference cell and degree for an example and should return a string of Python code
- that will create the example element.
-
- The additional inputs element and example may be needed in some more complex cases.
-
- This function must be implemented.
-
- Args:
- name: Implementation string as set in the .def file
- reference: The name of the reference cell
- degree: The degree of this example
- params: Additional parameters set in the .def file
- language: Programming language
- element: The DefElement element object
- example: Raw example data
-
- Returns:
- Example code
- """
- raise NotImplementedError()
-
- @classmethod
- def version(cls) -> str:
- """Get the version number of this implementation.
-
- This function must be implemented.
-
- Returns:
- Version number
- """
- raise NotImplementedError()
-
- @classmethod
- def install(cls, language: str) -> str | None:
- """Get the command(s) to install this implementation.
-
- Args:
- language: Programming language
-
- Returns:
- Version number
- """
- return None
-
- @classmethod
- def verify(
- cls,
- name: str,
- reference: str,
- degree: int,
- params: dict[str, str],
- element: Element,
- example: str,
- ) -> tuple[list[list[list[int]]], typing.Callable[[NDArray[float64]], NDArray[float64]]]:
- """Get information needed to run verification.
-
- Implementation of this function is optional, but it must be implemented for verification
- to be carried out. If this function is implemented, then the class variable `verification`
- should be set to `True` to activate verification.
-
- Args:
- name: Implementation string as set in the .def file
- reference: The name of the reference cell
- degree: The degree of this example
- params: Additional parameters set in the .def file
- element: The DefElement element object
- example: Raw example data
-
- Returns:
- This function returns two things:
- - A list of lists of lists that gives the degrees of freedom (DOFs) associated with
- each sub-entity. The entry `[i][j][k]` of this returned value is the `k`th dof that
- is associated with the `j`th sub-entity of dimension `i`.
- - A function thtat takes a set of points on the DefElement reference cell as input and
- returns the a Numpy array containing the values of the basis functions at every point.
- The entry `[i, j, k]` in this Numpy array is the `j`th component of the `k`th basis
- function evaluated at the `i`th point.
- """
- raise NotImplementedError()
-
- @classmethod
- def implemented(cls, element: Element) -> bool:
- """Check if an element is implemented.
-
- This can be used to overrule Element's implemented function.
-
- Implementation of this function is optional.
-
- Args:
- element: The element
-
- Returns:
- Example code
- """
- return True
-
- @classmethod
- def notes(cls, element: Element) -> list[str]:
- """Return a list of notes to include for the implementation of this element.
-
- Implementation of this function is optional.
-
- Args:
- element: Element data
-
- Returns:
- List of notes
- """
- return []
-
- @classmethod
- def references(cls, element: Element) -> list[dict[str, str]]:
- """Return a list of additional references to include for the implementation of this element.
-
- Implementation of this function is optional.
-
- Args:
- element: Element data
-
- Returns:
- List of references
- """
- return []
-
- @classmethod
- def examples(cls, element: Element, language: str) -> str:
- """Generate code for all examples.
-
- This function should not be overridden in subclasses.
-
- Args:
- element: The element
- language: Programming language
-
- Returns:
- Example code
- """
- if language not in cls.languages:
- raise ValueError(f"Implementation cannot create snippets for language: {language}")
- code = cls.example_import(language)
- assert cls.id is not None
- for eg in element.examples:
- reference, defelement_degree, variant, kwargs = parse_example(eg)
- try:
- if cls.id.startswith("*(") and cls.id.endswith(")"):
- name, degree, params = element.get_implementation_string(
- cls.id[2:-1].split(" -> ")[0],
- reference,
- defelement_degree,
- variant,
- )
- else:
- name, degree, params = element.get_implementation_string(
- cls.id, reference, defelement_degree, variant
- )
- except NotImplementedError:
- continue
-
- for i, j in kwargs.items():
- assert i not in params
- params[i] = j
-
- assert degree is not None
- code += "\n\n"
- if language in ["python", "julia"]:
- code += "# "
- elif language in ["rust", "c++", "c"]:
- code += "// "
- else:
- raise ValueError(f"Unsupported language: {language}")
- code += "Create "
- if variant is None:
- code += element.name
- else:
- code += element.name_with_variant(variant)
- code += f" degree {degree} on a {reference}\n"
- code += cls.single_example(name, reference, degree, params, language, element, eg)
- return code
-
- # Unique identifier used in implementation section of .def files
- id: str
- # The name of the implementation
- name: str
- # URL of source of implementation (eg Github link)
- url: str
- # Set to true if this implementation should be verified
- verification = False
- # Language(s) that this implementation can create snippets for
- languages: tuple[str, ...]
- # Language to pass into install command to get command(s) to install
- # Note that this only needs to be set if len(languages) > 1
- install_language: str | None = None
-
-
-class VariantNotImplemented(NotImplementedError):
- """Error for variants that are not implemented."""
-
-
-class DegreeNotImplemented(NotImplementedError):
- """Error for degrees that are not implemented."""
-
-
-class NotImplementedOnReference(NotImplementedError):
- """Error for element not implemented on a reference cell."""
-
-
-ValueType = int | str | list["ValueType"]
-
-
-def _parse_value(v: str) -> ValueType:
- """Parse a string.
-
- Args:
- v: String
-
- Returns:
- Parsed string
- """
- v = v.strip()
- if v[0] == "[" and v[-1] == "]":
- return [_parse_value(i) for i in v[1:-1].split(";")]
- if re.match(r"[0-9]+$", v):
- return int(v)
- return v
-
-
-def parse_example(
- e: str,
-) -> tuple[str, int, str | None, dict[str, int | str | list[ValueType]]]:
- """Parse an example.
-
- Args:
- e: The example
-
- Returns:
- Parsed example information
- """
- if " {" in e:
- e, rest = e.split(" {")
- rest = rest.split("}")[0]
- while re.search(r"\[([^\]]*),", rest):
- rest = re.sub(r"\[([^\]]*),", r"[\1;", rest)
- kwargs = {}
- for i in rest.split(","):
- key, value = i.split("=")
- kwargs[key] = _parse_value(value)
- else:
- kwargs = {}
- s = e.split(",")
- if len(s) == 3:
- ref, degree, variant = s
- else:
- ref, degree = e.split(",")
- variant = None
- return ref, int(degree), variant, kwargs
diff --git a/defelement/implementations/ferrite.py b/defelement/implementations/ferrite.py
deleted file mode 100644
index 32fee532ec..0000000000
--- a/defelement/implementations/ferrite.py
+++ /dev/null
@@ -1,107 +0,0 @@
-"""Implementation in Ferrite.jl."""
-
-import re
-import typing
-
-from defelement.element import Element
-from defelement.implementations.core import Implementation, NotImplementedOnReference
-
-# DefElement reference cell names mapped to the Ferrite.jl reference shapes
-reference_shapes = {
- "interval": "RefLine",
- "triangle": "RefTriangle",
- "quadrilateral": "RefQuadrilateral",
- "tetrahedron": "RefTetrahedron",
- "hexahedron": "RefHexahedron",
- "prism": "RefPrism",
- "pyramid": "RefPyramid",
-}
-
-# The registry file that Julia's package manager resolves Ferrite's versions from
-versions_url = (
- "https://raw.githubusercontent.com/JuliaRegistries/General/master/F/Ferrite/Versions.toml"
-)
-
-
-class FerriteImplementation(Implementation):
- """Implementation in Ferrite.jl."""
-
- @classmethod
- def format(cls, string: str, params: dict[str, typing.Any]) -> str:
- """Format implementation string."""
- out = string
- for p, v in params.items():
- if p == "vdim":
- out += f"^{v}"
- else:
- raise ValueError(f"Unexpected parameter: {p}")
- return out
-
- @classmethod
- def example_import(cls, language: str) -> str:
- """Get imports to include at start of example."""
- if language != "julia":
- raise ValueError(f"Unsupported language: {language}")
- return "using Ferrite"
-
- @classmethod
- def single_example(
- cls,
- name: str,
- reference: str,
- degree: int,
- params: dict[str, str],
- language: str,
- element: Element,
- example: str,
- ) -> str:
- """Generate code for a single example."""
- if language != "julia":
- raise ValueError(f"Unsupported language: {language}")
- if reference not in reference_shapes:
- raise NotImplementedOnReference()
- out = f"ip = {name}{{{reference_shapes[reference]}, {degree}}}()"
- for p, v in params.items():
- if p == "vdim":
- out += f"^{v}"
- else:
- raise ValueError(f"Unexpected parameter: {p}")
- return out
-
- @classmethod
- def install(cls, language: str) -> str | None:
- """Get the command(s) to install this implementation."""
- if language == "julia":
- return "julia -e 'using Pkg; Pkg.add(\"Ferrite\")'"
- return None
-
- @classmethod
- def version(cls) -> str:
- """Get the version number of this implementation."""
- import requests
-
- # Ferrite is not registered on PyPI, so the latest release is read from the entries of
- # Julia's General registry, which are of the form `["1.2.3"]`
- versions = re.findall(
- r'^\["([0-9]+(?:\.[0-9]+)*)"\]', requests.get(versions_url).text, re.MULTILINE
- )
- return max(versions, key=lambda v: tuple(int(i) for i in v.split(".")))
-
- @classmethod
- def notes(cls, element: Element) -> list[str]:
- """Return a list of notes to include for the implementation of this element."""
- if element.filename == "serendipity":
- return [
- (
- "Ferrite.jl uses point evaluations at the midpoints of the edges of the cell "
- "in place of the integral moments used in DefElement's definition of this "
- "element. Its basis functions therefore differ from the ones shown here, "
- "although they span the same space."
- )
- ]
- return []
-
- id = "ferrite"
- name = "Ferrite.jl"
- url = "https://github.com/Ferrite-FEM/Ferrite.jl"
- languages = ("julia",)
diff --git a/defelement/implementations/fiat.py b/defelement/implementations/fiat.py
deleted file mode 100644
index 2370c80e37..0000000000
--- a/defelement/implementations/fiat.py
+++ /dev/null
@@ -1,241 +0,0 @@
-"""FIAT implementation."""
-
-import typing
-
-import sympy
-from numpy import float64
-from numpy.typing import NDArray
-
-from defelement.element import Element
-from defelement.implementations.core import (
- Implementation,
- parse_example,
- pypi_name,
-)
-
-# TODO make this a FIAT attribute
-true_space_dimension = {
- "Bell": 18,
- "Mardal-Tai-Winther": 9,
- "reduced Hsieh-Clough-Tocher": 9,
- "Arnold-Winther": 24,
- "nonconforming Arnold-Winther": 15,
-}
-
-
-@pypi_name("firedrake-fiat")
-class FIATImplementation(Implementation):
- """FIAT implementation."""
-
- @classmethod
- def format(cls, string: str, params: dict[str, typing.Any]) -> str:
- """Format implementation string."""
- out = f"FIAT.{string}"
- started = False
- for p, v in params.items():
- if p == "variant":
- if not started:
- out += "(..."
- started = True
- out += f', {p}="{v}"'
- elif p in ["subdegree", "reduced"]:
- if not started:
- out += "(..."
- started = True
- out += f", {p}={v}"
- elif p != "degree":
- raise ValueError(f"Unexpected parameter: {p}")
- if started:
- out += ")"
- return out
-
- @classmethod
- def example_import(cls, language: str) -> str:
- """Get imports to include at start of example."""
- return "import FIAT"
-
- @classmethod
- def single_example(
- cls,
- name: str,
- reference: str,
- degree: int,
- params: dict[str, str],
- language: str,
- element: Element,
- example: str,
- ) -> str:
- """Generate code for a single example."""
- if reference in ["interval", "triangle", "tetrahedron"]:
- cell = f'FIAT.ufc_cell("{reference}")'
- elif reference == "quadrilateral":
- cell = "FIAT.reference_element.UFCQuadrilateral()"
- elif reference == "hexahedron":
- cell = "FIAT.reference_element.UFCHexahedron()"
- else:
- raise ValueError(f"Unsupported cell: {reference}")
- out = f"element = FIAT.{name}({cell}"
- if params.get("degree", "") != "None":
- out += f", {degree}"
- for i, j in params.items():
- if i == "variant":
- out += f', {i}="{j}"'
- if i == "subdegree":
- subdegree = parse_example(example)[1]
- out += f", {i}={sympy.S(j).subs(sympy.Symbol('k'), subdegree)}"
- if i == "reduced":
- out += f", {i}={j}"
- out += ")"
- return out
-
- @classmethod
- def verify(
- cls,
- name: str,
- reference: str,
- degree: int,
- params: dict[str, str],
- element: Element,
- example: str,
- ) -> tuple[list[list[list[int]]], typing.Callable[[NDArray[float64]], NDArray[float64]]]:
- """Get verification data."""
- import FIAT
-
- if reference in ["interval", "triangle", "tetrahedron"]:
- cell = FIAT.ufc_cell(reference)
- elif reference == "quadrilateral":
- cell = FIAT.reference_element.UFCQuadrilateral()
- elif reference == "hexahedron":
- cell = FIAT.reference_element.UFCHexahedron()
- else:
- raise ValueError(f"Unsupported cell: {reference}")
-
- args = []
- kwargs: dict[str, typing.Any] = {}
-
- if params.get("degree", "") != "None":
- args.append(degree)
-
- if "variant" in params:
- kwargs["variant"] = params["variant"]
-
- if "reduced" in params:
- kwargs["reduced"] = bool(params["reduced"])
-
- e = getattr(FIAT, name)(cell, *args, **kwargs)
-
- value_size = 1
- for i in e.value_shape():
- value_size *= i
- edofs = [list(i.values()) for i in e.entity_dofs().values()]
-
- if reference == "triangle":
- edofs[1] = [edofs[1][2], edofs[1][1], edofs[1][0]]
- elif reference == "tetrahedron":
- edofs[1] = [
- edofs[1][5],
- edofs[1][4],
- edofs[1][3],
- edofs[1][2],
- edofs[1][1],
- edofs[1][0],
- ]
- edofs[2] = [edofs[2][3], edofs[2][2], edofs[2][1], edofs[2][0]]
- elif reference == "quadrilateral":
- edofs = [
- [edofs[0][0], edofs[0][2], edofs[0][1], edofs[0][3]],
- [edofs[1][2], edofs[1][0], edofs[1][1], edofs[1][3]],
- [edofs[2][0]],
- ]
- elif reference == "hexahedron":
- edofs = [
- [
- edofs[0][0],
- edofs[0][4],
- edofs[0][2],
- edofs[0][6],
- edofs[0][1],
- edofs[0][5],
- edofs[0][3],
- edofs[0][7],
- ],
- [
- edofs[1][8],
- edofs[1][4],
- edofs[1][0],
- edofs[1][6],
- edofs[1][2],
- edofs[1][10],
- edofs[1][1],
- edofs[1][3],
- edofs[1][9],
- edofs[1][5],
- edofs[1][7],
- edofs[1][11],
- ],
- [
- edofs[2][4],
- edofs[2][2],
- edofs[2][0],
- edofs[2][1],
- edofs[2][3],
- edofs[2][5],
- ],
- [edofs[3][0]],
- ]
-
- sd = cell.get_spatial_dimension()
- if element.name in {
- "Bernardi-Raugel",
- "Guzman-Neilan (first kind)",
- "Guzman-Neilan (second kind)",
- }:
- reduced_dim = e.space_dimension() - (sd + 1) * (sd - 1)
- else:
- reduced_dim = true_space_dimension.get(element.name)
-
- if reduced_dim is not None:
- for dim in range(len(edofs)):
- for i in range(len(edofs[dim])):
- edofs[dim][i] = [dof for dof in edofs[dim][i] if dof < reduced_dim]
-
- z = (0,) * sd
- return edofs, lambda points: e.tabulate(0, points)[z][slice(reduced_dim)].T.reshape(
- points.shape[0], value_size, -1
- )
-
- @classmethod
- def notes(cls, element: Element) -> list[str]:
- """Return a list of notes to include for the implementation of this element."""
- if element.name in true_space_dimension:
- return [
- (
- "This implementation includes additional DOFs that are used then filtered "
- "out when mapping the element, as described in Kirby (2018)."
- )
- ]
- return []
-
- @classmethod
- def references(cls, element: Element) -> list[dict[str, typing.Any]]:
- """Return a list of additional references to include for the implementation of this element."""
- if element.name in true_space_dimension:
- return [
- {
- "title": "A general approach to transforming finite elements",
- "author": ["Kirby, Robert C."],
- "year": 2018,
- "journal": "SMAI Journal of Computational Mathematics",
- "volume": 4,
- "pagestart": 197,
- "pageend": 224,
- "doi": "10.5802/smai-jcm.33",
- }
- ]
- return []
-
- id = "fiat"
- name = "FIAT"
- url = "https://github.com/firedrakeproject/fiat"
- verification = True
- languages = ("python",)
diff --git a/defelement/implementations/ndelement.py b/defelement/implementations/ndelement.py
deleted file mode 100644
index 01c35be045..0000000000
--- a/defelement/implementations/ndelement.py
+++ /dev/null
@@ -1,170 +0,0 @@
-"""Implementation in ndelement."""
-
-import typing
-
-from numpy import float64
-from numpy.typing import NDArray
-
-from defelement.element import Element
-from defelement.implementations.core import Implementation
-
-
-class NdelementImplementation(Implementation):
- """Implementation in ndelement."""
-
- @classmethod
- def version(cls) -> str:
- """Get the version number of this implementation."""
- from importlib.metadata import PackageNotFoundError, version
-
- try:
- return version("ndelement")
- except PackageNotFoundError:
- import requests
-
- return requests.get("https://pypi.org/pypi/ndelement/json").json()["info"]["version"]
-
- @classmethod
- def install(cls, language: str) -> str | None:
- """Get the command(s) to install this implementation."""
- if language == "python":
- return "pip install ndelement"
- if language == "rust":
- return f'ndelement = "{cls.version()}"'
- return None
-
- @classmethod
- def format(cls, string: str, params: dict[str, typing.Any]) -> str:
- """Format implementation string."""
- out = f"Family.{string}"
- for p, v in params.items():
- out += f", {p}="
- if p == "continuity":
- out += f"Continuity.{v}"
- else:
- raise ValueError(f"Unexpected parameter: {p}")
- return out
-
- @classmethod
- def example_import(cls, language: str) -> str:
- """Get imports to include at start of example."""
- if language == "python":
- return (
- "from ndelement import ciarlet\n"
- "from ndelement.reference_cell import ReferenceCellType"
- )
- if language == "rust":
- return "use ndelement::{ciarlet, types::{Continuity, ReferenceCellType}};"
- raise ValueError(f"Unsupported language: {language}")
-
- @classmethod
- def single_example_python(
- cls,
- name: str,
- reference: str,
- degree: int,
- params: dict[str, str],
- element: Element,
- example: str,
- ) -> str:
- """Generate Python code for a single example."""
- out = "family = ciarlet.create_family("
- out += f"ciarlet.Family.{name}, {degree}"
- if "continuity" in params:
- assert params["continuity"] in ["Standard", "Discontinuous"]
- out += f", continuity=ciarlet.Continuity.{params['continuity']}"
- out += ")\n"
- out += f"element = family.element(ReferenceCellType.{reference[0].upper() + reference[1:]})"
- return out
-
- @classmethod
- def single_example_rust(
- cls,
- name: str,
- reference: str,
- degree: int,
- params: dict[str, str],
- element: Element,
- example: str,
- ) -> str:
- """Generate Rust code for a single example."""
- out = f"let family = ciarlet::{name}ElementFamily({degree}, Continuity::"
- if "continuity" in params:
- assert params["continuity"] in ["Standard", "Discontinuous"]
- out += params["continuity"]
- else:
- out += "Standard"
- out += ");\n"
- out += f"let element = family.element(ReferenceCellType::{reference[0].upper() + reference[1:]});"
- return out
-
- @classmethod
- def single_example(
- cls,
- name: str,
- reference: str,
- degree: int,
- params: dict[str, str],
- language: str,
- element: Element,
- example: str,
- ) -> str:
- """Generate code for a single example."""
- if language == "python":
- return cls.single_example_python(name, reference, degree, params, element, example)
- if language == "rust":
- return cls.single_example_rust(name, reference, degree, params, element, example)
- raise ValueError(f"Unsupported language: {language}")
-
- @classmethod
- def verify(
- cls,
- name: str,
- reference: str,
- degree: int,
- params: dict[str, str],
- element: Element,
- example: str,
- ) -> tuple[list[list[list[int]]], typing.Callable[[NDArray[float64]], NDArray[float64]]]:
- """Get verification data."""
- from ndelement.ciarlet import Continuity, Family, create_family
- from ndelement.reference_cell import ReferenceCellType, entity_counts
-
- kwargs = {}
- if "continuity" in params:
- kwargs["continuity"] = getattr(Continuity, params["continuity"])
-
- cell = getattr(ReferenceCellType, reference[0].upper() + reference[1:])
- e = create_family(getattr(Family, name), degree, **kwargs).element(cell)
- entity_dofs = [
- [e.entity_dofs(dim, entity) for entity in range(n)]
- for dim, n in enumerate(entity_counts(cell))
- if n > 0
- ]
-
- if reference == "triangle":
- entity_dofs[1] = [entity_dofs[1][2], entity_dofs[1][1], entity_dofs[1][0]]
- elif reference == "tetrahedron":
- entity_dofs[1] = [
- entity_dofs[1][5],
- entity_dofs[1][4],
- entity_dofs[1][3],
- entity_dofs[1][2],
- entity_dofs[1][1],
- entity_dofs[1][0],
- ]
- entity_dofs[2] = [
- entity_dofs[2][3],
- entity_dofs[2][2],
- entity_dofs[2][1],
- entity_dofs[2][0],
- ]
-
- return entity_dofs, lambda points: e.tabulate(points, 0)[:, :, :, 0].transpose((2, 0, 1))
-
- id = "ndelement"
- name = "ndelement"
- url = "https://codeberg.org/nd-project/nd"
- verification = True
- languages = ("python", "rust")
- install_language = "python"
diff --git a/defelement/implementations/simplefem.py b/defelement/implementations/simplefem.py
deleted file mode 100644
index 0f9298cd99..0000000000
--- a/defelement/implementations/simplefem.py
+++ /dev/null
@@ -1,140 +0,0 @@
-"""Simplefem implementation."""
-
-#
-import typing
-
-from numpy import float64
-from numpy.typing import NDArray
-
-from defelement.element import Element
-from defelement.implementations.core import Implementation
-
-
-class SimplefemImplementation(Implementation):
- """Simplefem implementation."""
-
- #
-
- #
- @classmethod
- def format(cls, string: str, params: dict[str, typing.Any]) -> str:
- """Format implementation string."""
- return string
-
- #
-
- #
- @classmethod
- def example_import(cls, language: str) -> str:
- """Get imports to include at start of example."""
- return "import simplefem"
-
- @classmethod
- def single_example(
- cls,
- name: str,
- reference: str,
- degree: int,
- params: dict[str, str],
- language: str,
- element: Element,
- example: str,
- ) -> str:
- """Generate code for a single example."""
- return f"element = simplefem.{name}({degree})"
-
- #
-
- #
- @classmethod
- def install(cls, language: str) -> str | None:
- """Get the command(s) to install this implementation."""
- if language == "python":
- return "pip3 install git+https://github.com/DefElement/simplefem"
- return None
-
- #
-
- #
- @classmethod
- def version(cls) -> str:
- """Get the version number of this implementation."""
- import simplefem
-
- return simplefem.__version__
-
- #
-
- #
- @classmethod
- def verify(
- cls,
- name: str,
- reference: str,
- degree: int,
- params: dict[str, str],
- element: Element,
- example: str,
- ) -> tuple[list[list[list[int]]], typing.Callable[[NDArray[float64]], NDArray[float64]]]:
- """Get verification data."""
- #
- #
- import numpy as np
- import simplefem
- #
-
- #
- e = getattr(simplefem, name)(degree)
- #
-
- #
- entity_dofs: list[list[list[int]]] = [[[], [], []], [[], [], []], [[]]]
-
- for i, p in enumerate(e.evaluation_points):
- # DOFs associated with vertices
- if np.allclose(p, [-1, 0]):
- entity_dofs[0][0].append(i)
- elif np.allclose(p, [1, 0]):
- entity_dofs[0][1].append(i)
- elif np.allclose(p, [0, 1]):
- entity_dofs[0][2].append(i)
- # DOFs associated with edges
- elif np.isclose(p[1], 0):
- entity_dofs[1][0].append(i)
- elif np.isclose(p[1] - p[0], 1):
- entity_dofs[1][1].append(i)
- elif np.isclose(p[1] + p[0], 1):
- entity_dofs[1][2].append(i)
- # DOFs associated with interior of cell
- else:
- entity_dofs[2][0].append(i)
- #
-
- #
- def tabulate(points):
- mapped_points = np.array([[2 * p[0] + p[1] - 1, p[1]] for p in points])
- table = np.zeros([points.shape[0], 1, degree])
-
- for i, p in enumerate(mapped_points):
- for j in range(degree):
- table[i, 0, j] = e.evaluate(j, p)[0]
- return table
-
- #
-
- #
- return entity_dofs, tabulate
-
- #
-
- #
- id = "simplefem"
- name = "simplefem"
- url = "https://github.com/DefElement/simplefem"
- languages = ("python",)
- #
- #
- verification = True
-
-
-#
diff --git a/defelement/implementations/symfem.py b/defelement/implementations/symfem.py
deleted file mode 100644
index b785e8b688..0000000000
--- a/defelement/implementations/symfem.py
+++ /dev/null
@@ -1,148 +0,0 @@
-"""Symfem implementation."""
-
-import typing
-
-from numpy import float64
-from numpy.typing import NDArray
-from symfem.finite_element import FiniteElement
-
-from defelement.element import Element
-from defelement.implementations.core import (
- Implementation,
- parse_example,
- pypi_name,
-)
-from defelement.tools import to_array
-
-
-def symfem_create_element(element: Element, example: str) -> FiniteElement:
- """Create a Symfem element.
-
- Args:
- element: Element info
- example: The example
-
- Returns:
- Symfem element
- """
- import symfem
-
- ref, defelement_deg, variant, _kwargs = parse_example(example)
- symfem_name, deg, params = element.get_implementation_string(
- "symfem", ref, defelement_deg, variant
- )
- assert symfem_name is not None
- if ref == "dual polygon":
- ref += "(4)"
- assert isinstance(deg, int)
- return symfem.create_element(ref, symfem_name, deg, **params)
-
-
-class CachedSymfemTabulator:
- """Symfem tabulator with caching."""
-
- def __init__(self, element: FiniteElement):
- """Initialise.
-
- Args:
- element: Symfem element
- """
- self.element = element
- self.tables: list[tuple[NDArray[float64], NDArray[float64]]] = []
-
- def tabulate(self, points: NDArray[float64]) -> NDArray[float64]:
- """Tabulate this element.
-
- Args:
- points: Points to tabulate at
-
- Returns:
- Values of basis functions
- """
- import numpy as np
-
- for i, j in self.tables:
- if i.shape == points.shape and np.allclose(i, points):
- return j
- shape = (points.shape[0], self.element.range_dim, self.element.space_dim)
- table = to_array(self.element.tabulate_basis(points, "xx,yy,zz")) # type: ignore
- assert not isinstance(table, float)
- table = table.reshape(shape)
- self.tables.append((points, table))
- return table
-
-
-@pypi_name("symfem")
-class SymfemImplementation(Implementation):
- """Symfem implementation."""
-
- @classmethod
- def format(cls, string: str, params: dict[str, typing.Any]) -> str:
- """Format implementation string."""
- out = f'"{string}"'
- for p, v in params.items():
- if p == "variant":
- out += f', {p}="{v}"'
- else:
- raise ValueError(f"Unexpected parameter: {p}")
- return out
-
- @classmethod
- def example_import(cls, language: str) -> str:
- """Get imports to include at start of example."""
- return "import symfem"
-
- @classmethod
- def single_example(
- cls,
- name: str,
- reference: str,
- degree: int,
- params: dict[str, str],
- language: str,
- element: Element,
- example: str,
- ) -> str:
- """Generate examples."""
- out = "element = symfem.create_element("
- if reference == "dual polygon":
- out += f'"{reference}(4)",'
- else:
- out += f'"{reference}",'
- out += f' "{name}", {degree}'
- for i, j in params.items():
- if isinstance(j, str):
- out += f', {i}="{j}"'
- else:
- out += f", {i}={j}"
- out += ")"
- return out
-
- @classmethod
- def verify(
- cls,
- name: str,
- reference: str,
- degree: int,
- params: dict[str, str],
- element: Element,
- example: str,
- ) -> tuple[list[list[list[int]]], typing.Callable[[NDArray[float64]], NDArray[float64]]]:
- """Get verification data."""
- import symfem
-
- if reference == "dual polygon":
- reference += "(4)"
- e = symfem.create_element(reference, name, degree, **params) # type: ignore
- edofs = [
- [e.entity_dofs(i, j) for j in range(e.reference.sub_entity_count(i))]
- for i in range(e.reference.tdim + 1)
- ]
- t = CachedSymfemTabulator(e)
- return edofs, lambda points: t.tabulate(points)
-
- id = "symfem"
- name = "Symfem"
- url = "https://github.com/mscroggs/symfem"
- verification = True
- languages = ("python",)
diff --git a/defelement/info.py b/defelement/info.py
deleted file mode 100644
index 97173daf3b..0000000000
--- a/defelement/info.py
+++ /dev/null
@@ -1,13 +0,0 @@
-"""Information about DefElement."""
-
-import os
-
-from defelement import settings
-
-number_of_elements = len(
- [
- file
- for file in os.listdir(settings.element_path)
- if file.endswith(".def") and not file.startswith(".")
- ]
-)
diff --git a/defelement/languages.py b/defelement/languages.py
deleted file mode 100644
index bd38cfd211..0000000000
--- a/defelement/languages.py
+++ /dev/null
@@ -1,145 +0,0 @@
-"""Programming languages."""
-
-import sys
-from inspect import isclass
-
-from webtools.code_markup import cpp_highlight, python_highlight, rust_highlight
-
-from defelement.implementations import Implementation
-
-
-def julia_highlight(code: str) -> str:
- """Highlight comments in a Julia snippet and convert it to HTML.
-
- webtools does not provide a Julia highlighter. The snippets are short enough that
- keyword highlighting adds little, so only comments are highlighted, using the same
- color that webtools uses.
-
- Args:
- code: Julia snippet
-
- Returns:
- Snippet with comments highlighted
- """
- out = []
- for line in code.replace(" ", " ").split("\n"):
- if "#" in line:
- line, comment = line.split("#", 1)
- line += f"#{comment}"
- out.append(line)
- return " ".join(out)
-
-
-class Language:
- """A programming language."""
-
- @classmethod
- def highlight(cls, code: str) -> str:
- """Add code highlighting.
-
- Args:
- code: Some code
-
- Returns:
- Code with HTML formatting
- """
- raise NotImplementedError()
-
- @classmethod
- def install(cls, impl: type[Implementation]) -> str:
- """Generate installation information for a language.
-
- Args:
- impl: The implementation
-
- Returns:
- Installation info
- """
- info = f"Before running this example, you must install {impl.name}"
-
- cmd = impl.install(cls.id)
-
- if cmd is None:
- info += ". "
- else:
- info += ":
" + cmd.replace("\n", " ") + "
"
- return info
-
- # Javascript-friendly id
- id: str
- # Human-readable name of language
- name: str
-
-
-class Python(Language):
- """Python."""
-
- @classmethod
- def highlight(cls, code: str) -> str:
- """Add code highlighting."""
- return python_highlight(code)
-
- id = "python"
- name = "Python"
-
-
-class Rust(Language):
- """Rust."""
-
- @classmethod
- def highlight(cls, code: str) -> str:
- """Add code highlighting."""
- return rust_highlight(code)
-
- @classmethod
- def install(cls, impl: type[Implementation]) -> str:
- """Generate installation information for a language."""
- info = (
- f"To running this snippet, you must add {impl.name}"
- " to your Cargo.toml file"
- )
-
- cmd = impl.install("rust")
-
- if cmd is None:
- info += ". "
- else:
- info += ":
" + cmd.replace("\n", " ") + "
"
- return info
-
- id = "rust"
- name = "Rust"
-
-
-class Cpp(Language):
- """C++."""
-
- @classmethod
- def highlight(cls, code: str) -> str:
- """Add code highlighting."""
- return cpp_highlight(code)
-
- id = "cpp"
- name = "C++"
-
-
-class Julia(Language):
- """Julia."""
-
- @classmethod
- def highlight(cls, code: str) -> str:
- """Add code highlighting."""
- return julia_highlight(code)
-
- id = "julia"
- name = "Julia"
-
-
-this = sys.modules[__name__]
-
-languages = {}
-
-for item in dir():
- lang = getattr(this, item)
- if isclass(lang) and issubclass(lang, Language) and lang != Language:
- languages[lang.id] = lang
diff --git a/defelement/markup.py b/defelement/markup.py
deleted file mode 100644
index 7e652e7e32..0000000000
--- a/defelement/markup.py
+++ /dev/null
@@ -1,151 +0,0 @@
-"""Markup."""
-
-import re
-import typing
-
-import symfem
-from webtools import settings
-from webtools.code_markup import code_highlight as _code_highlight
-from webtools.markup import insert_links as _insert_links
-
-from defelement import citations, info, plotting, symbols
-
-page_references: list[str] = []
-
-
-def insert_links(txt: str, root_dir: str = "") -> str:
- """Insert links.
-
- Args:
- txt: text
-
- Returns:
- Text with links
- """
- txt = re.sub(r"\(element::([^\)]+)\)", r"(/elements/\1.html)", txt)
- txt = re.sub(r"\(reference::([^\)]+)\)", r"(/lists/references/\1.html)", txt)
- txt = txt.replace("(index::all)", "(/elements/index.html)")
- txt = txt.replace("(index::families)", "(/families/index.html)")
- txt = txt.replace("(index::recent)", "(/lists/recent.html)")
- txt = re.sub(r"\(index::([^\)]+)::([^\)]+)\)", r"(/lists/\1/\2.html)", txt)
- txt = re.sub(r"\(index::([^\)]+)\)", r"(/lists/\1)", txt)
- return _insert_links(txt, root_dir)
-
-
-def plot_element(matches: typing.Match[str]) -> str:
- """Plot element.
-
- Args:
- matches: Element data
-
- Returns:
- HTML for plot of element
- """
- if "variant=" in matches[1]:
- a, b = matches[1].split(" variant=")
- e = symfem.create_element(a, matches[2], int(matches[3]), variant=b)
- else:
- e = symfem.create_element(matches[1], matches[2], int(matches[3]))
-
- return f"
{''.join([plotting.plot_function(e, i) for i in range(e.space_dim)])}
"
-
-
-def plot_single_element(matches: typing.Match[str]) -> str:
- """Plot a single element.
-
- Args:
- matches: Element data
-
- Returns:
- HTML for plot of element
- """
- if "variant=" in matches[1]:
- a, b = matches[1].split(" variant=")
- e = symfem.create_element(a, matches[2], int(matches[3]), variant=b)
- else:
- e = symfem.create_element(matches[1], matches[2], int(matches[3]))
-
- return f"
"
-
- with open(os.path.join(settings.htmlimg_path, f"{filename}.html"), "w") as f:
- f.write(make_html_page(img_page))
- all_plots.append(filename)
-
- if link:
- return f""
- else:
- return f""
-
-
-def plot_reference(ref, link: bool = True) -> str:
- """Plot a reference cell.
-
- Args:
- link: Should a link be included?
-
- Returns:
- HTML for plot
- """
- if ref.name == "dual polygon":
- assert isinstance(ref, symfem.references.DualPolygon)
- ref_id = f"dual-polygon-{ref.number_of_triangles}"
- else:
- ref_id = ref.name
-
- filename = f"ref-{ref_id}"
- desc = f"{ref.name} reference cell"
-
- return do_the_plot(
- filename,
- desc,
- ref.plot_entity_diagrams,
- png_width=175 * (ref.tdim + 1),
- scale=300,
- link=link,
- )
-
-
-def plot_function(element: FiniteElement, dof_i: int, link: bool = True) -> str:
- """Plot a functions.
-
- Args:
- element: The element
- dof_i: The DOF index
- link: Should a link be included?
-
- Returns:
- HTML for plot
- """
- if element.reference.name == "dual polygon":
- ref = element.reference
- assert isinstance(ref, symfem.references.DualPolygon)
- ref_id = f"dual-polygon-{ref.number_of_triangles}"
- else:
- ref_id = element.reference.name
-
- desc = f"Basis function in a {element.name} space"
- filename = f"element-{element.name}"
- for i, j in element.init_kwargs().items():
- filename += f"-{i}-{j}"
- filename += f"-{ref_id}-{element.order}-{dof_i}"
- return do_the_plot(
- filename,
- desc,
- element.plot_basis_function,
- [dof_i],
- link=link,
- cache_element=element,
- )
-
-
-def plot_basis_functions(element: FiniteElement, link: bool = True) -> list[str | None]:
- """Plot basis functions of an element.
-
- Args:
- element: The element
- link: Should a link be included?
-
- Returns:
- HTML for plot
- """
- if element.range_dim == 1:
- if element.domain_dim > 2:
- return [None for i in range(element.space_dim)]
- else:
- if element.range_dim != element.domain_dim:
- return [None for i in range(element.space_dim)]
-
- return [plot_function(element, i, link=link) for i in range(element.space_dim)]
-
-
-def _parse_point(points: list[str], n: int) -> tuple[float, float]:
- """Parge a point.
-
- Args:
- points: Point data
- n: Point number
-
- Returns:
- Point
- """
- point = points[n].strip()
- if point == "cycle":
- assert n > 0
- return _parse_point(points, 0)
- assert point[0] == "(" and point[-1] == ")"
-
- x, y = point[1:-1].split(",")
- return float(x) / 100, float(y) / 100
-
-
-def plot_img(img_filename: str, link: bool = True) -> str:
- """Plot a image.
-
- Args:
- img_filename: Image filename
- link: Should a link be included?
-
- Returns:
- HTML for plot
- """
- metadata = {"DESC": ""}
- filename = f"img-{img_filename}"
- with open(os.path.join(settings.img_path, f"{img_filename}.img")) as f:
- for line in f:
- if ":" in line:
- a, b = line.split(":", 1)
- metadata[a.strip()] = b.strip()
- desc = metadata["DESC"]
-
- def actual_plot(
- filename: str,
- plot_options: dict[str, typing.Any] | None = None,
- **kwargs: typing.Any,
- ):
- if plot_options is None:
- plot_options = {}
- img = Picture(**kwargs)
- colors = img.colors
- with open(os.path.join(settings.img_path, f"{img_filename}.img")) as f:
- for line in f:
- if ":" not in line:
- line = line.split("#")[0]
- line = line.strip()
- color = "black"
- if line.startswith("["):
- color, line = line[1:].split("]", 1)
- line = line.strip()
- if hasattr(colors, color.upper()):
- color = getattr(colors, color.upper())
-
- points = line.split("--")
- for i in range(len(points) - 1):
- p1 = _parse_point(points, i)
- p2 = _parse_point(points, i + 1)
- img.add_line(p1, p2, color=color, width=2)
- img.save(filename, plot_options)
-
- return do_the_plot(filename, desc, actual_plot, link=link)
-
-
-def plot_dof_diagram(element: FiniteElement, link: bool = True) -> str:
- """Plot a DOF diagram.
-
- Args:
- element: The element
- link: Should a link be included?
-
- Returns:
- HTML for plot
- """
- if element.reference.name == "dual polygon":
- ref = element.reference
- assert isinstance(ref, symfem.references.DualPolygon)
- ref_id = f"dual-polygon-{ref.number_of_triangles}"
- else:
- ref_id = element.reference.name
- desc = "DOFs of "
- desc += "an" if element.name.lower()[0] in "aieou" else "a"
- desc += f" {element.name} element"
- filename = f"element-{element.name}"
- for i, j in element.init_kwargs().items():
- filename += f"-{i}-{j}"
- filename += f"-{ref_id}-{element.order}-dofs"
- return do_the_plot(
- filename,
- desc,
- element.plot_dof_diagram,
- link=link,
- cache_element=element,
- )
diff --git a/defelement/polyset.py b/defelement/polyset.py
deleted file mode 100644
index bef8bf487c..0000000000
--- a/defelement/polyset.py
+++ /dev/null
@@ -1,201 +0,0 @@
-"""Polynomial sets."""
-
-import os
-import re
-import typing
-
-import yaml
-
-with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../data/polysets")) as f:
- poly_sets = yaml.load(f, Loader=yaml.FullLoader)
-
-named: dict[str, tuple[str, str, dict[str, str]]] = {}
-defs: dict[str, str] = {}
-
-
-def make_name(i: int) -> str:
- """Make a polyset name.
-
- Args:
- i: Polyset id
-
- Returns:
- Polyset name
- """
- return f"\\mathcal{{Z}}^{{({i})}}"
-
-
-def replace_def(matches: typing.Match[str]) -> str:
- """Replace def.
-
- Args:
- matches: TODO
-
- Returns:
- TODO
- """
- defs[matches[1]] = matches[2]
- return ""
-
-
-def replace_defmath(matches: typing.Match[str]) -> str:
- """Replace def.
-
- Args:
- matches: TODO
-
- Returns:
- TODO
- """
- defs[matches[1]] = f"\\({matches[2]}\\)"
- return ""
-
-
-def _get_match(regex: str, string: str, index: int) -> str:
- """Get a regular expression match.
-
- Args:
- regex: Regular expression
- string: Search string
- index: Index
-
- Return: Match
- """
- m = re.match(regex, string)
- assert m is not None
- return m[index]
-
-
-def make_poly_set(p: str) -> str:
- """Make a polynomial set.
-
- Args:
- p: Polyset data
-
- Returns:
- Formatted polynomial set
- """
- global defs
- if "&&" in p:
- return " \\oplus ".join([make_poly_set(i.strip()) for i in p.split("&&")])
- p = p.strip()
- if re.match(r"^\<([^\]]+)\>\[(.+)\]$", p):
- degree = _get_match(r"^\<([^\]]+)\>\[(.+)\]$", p, 1)
- the_set = _get_match(r"^\<([^\]]+)\>\[(.+)\]$", p, 2)
- defs = {}
- the_set_out = re.sub(r"\@def\@([^\@]+)\@([^\@]+)\@", replace_def, the_set)
- the_set_out = re.sub(r"\@defmath\@([^\@]+)\@([^\@]+)\@", replace_defmath, the_set_out)
- if the_set not in named:
- named[the_set] = (make_name(len(named)), the_set_out, defs)
- return f"{named[the_set][0]}_{{{degree}}}"
- if re.match(r"^\<([^\]]+)\>\[(.+)\]\^d$", p):
- degree = _get_match(r"^\<([^\]]+)\>\[(.+)\]\^d$", p, 1)
- the_set = _get_match(r"^\<([^\]]+)\>\[(.+)\]\^d$", p, 2)
- defs = {}
- the_set_out = re.sub(r"\@def\@([^\@]+)\@([^\@]+)\@", replace_def, the_set)
- the_set_out = re.sub(r"\@defmath\@([^\@]+)\@([^\@]+)\@", replace_defmath, the_set_out)
- if the_set not in named:
- named[the_set] = (make_name(len(named)), the_set_out, defs)
- return f"\\left({named[the_set][0]}_{{{degree}}}\\right)^d"
- for i, (j, k) in poly_sets.items():
- if re.match(rf"^{i}\[([^\]]+)\]$", p):
- degree = _get_match(rf"^{i}\[([^\]]+)\]$", p, 1)
- return f"{j}_{{{degree}}}"
- if re.match(rf"^{i}\[([^\]]+)\]\^dd$", p):
- degree = _get_match(rf"^{i}\[([^\]]+)\]\^dd$", p, 1)
- return f"{j}_{{{degree}}}^{{d\\times d}}"
- if re.match(rf"^{i}\[([^\]]+)\]\^d$", p):
- degree = _get_match(rf"^{i}\[([^\]]+)\]\^d$", p, 1)
- return f"{j}_{{{degree}}}^d"
- if re.match(rf"^{i}\[([^\]]+)\]\(([^\)]+)\)$", p):
- degree = _get_match(rf"^{i}\[([^\]]+)\]\(([^\)]+)\)$", p, 1)
- dim = _get_match(rf"^{i}\[([^\]]+)\]\(([^\)]+)\)$", p, 2)
- return f"{j}_{{{degree}}}^d(\\mathbb{{R}}^{{{dim}}})"
- raise ValueError(f"Unknown polynomial set: {p}")
-
-
-def make_extra_info(p: str) -> str:
- """Make extra info.
-
- Args:
- p: Polyset data
-
- Returns:
- Extra info
- """
- done = []
- out = []
- for a in p.split("&&"):
- a = a.strip()
- if re.match(r"^\<([^\]]+)\>\[(.+)\](?:\^d)?$", a):
- the_set = _get_match(r"^\<([^\]]+)\>\[(.+)\](?:\^d)?$", a, 2)
- if named[the_set] not in done:
- def_txt = ""
- if len(named[the_set][2]) > 0:
- def_txt = " where
"
- def_txt += "\n".join(
- [f"
\\({i}\\) is {j}
" for i, j in named[the_set][2].items()]
- )
- def_txt += "
"
- out.append(
- f"\\({named[the_set][0]}_k={insert_terms(named[the_set][1])}\\){def_txt}"
- )
- done.append(named[the_set])
- for i, (j, k) in poly_sets.items():
- if f"{{{{{i}[" in a and i not in done:
- out.append(f"\\({j}_k={k}\\)")
- done.append(i)
- continue
- for i, (j, k) in poly_sets.items():
- if re.match(rf"^{i}\[([^\]]+)\]\(([^\)]+)\)$", p):
- if i + "(d)" not in done:
- out.append(f"\\({j}_k(\\mathbb{{R}}^d)={k}\\)")
- done.append(i)
- break
- if re.match(rf"^{i}\[([^\]]+)\](?:\^d+)?$", a):
- if i not in done:
- out.append(f"\\({j}_k={k}\\)")
- done.append(i)
- break
- else:
- raise ValueError(f"Unknown polynomial set: {a}")
- return "
[view a list of all pages on DefElement](/sitemap.html)
-
diff --git a/pages/adding-an-implementation.md b/pages/adding-an-implementation.md
deleted file mode 100644
index 3d0e283fc9..0000000000
--- a/pages/adding-an-implementation.md
+++ /dev/null
@@ -1,251 +0,0 @@
---
-authors:
- - Scroggs, Matthew W.
---
-
-# Adding an implementation to DefElement
-
-This walkthrough will take you through all the steps involved in adding an implementation to
-DefElement.
-
-Detailed documentation of the class methods and variables used in this walkthrough, as well as
-other optional method, can be found in the file
-[defelement/implementations/core.py](https://github.com/DefElement/DefElement/tree/main/defelement/implementations/core.py).
-
-## simplefem
-As an example, this walkthrough will look at adding the
-[simplefem](https://github.com/DefElement/simplefem) library to DefElement.
-simplefem has been created to serve as an illustration library for this walkthrough, and should not be considered as a fully fledged library.
-It only features Lagrange elements on triangles and can evaluate their basis functions.
-For example, the following
-snippet will create a Lagrange element on a triangle with 10 basis functions and evaluate its basis
-function with index 5 at the point (0.3, 0.1):
-
-```python
-import simplefem
-import numpy as np
-
-e = simplefem.lagrange_element(10)
-value = e.evaluate(5, np.array([0.3, 0.1]))
-```
-
-## Adding simplefem snippets to element pages
-To add simplefem to DefElement, a file in the folder
-[defelement/implementations](https://github.com/DefElement/DefElement/tree/main/defelement/implementations)
-must be created. In this case, we called the file
-[simplefem.py](https://github.com/DefElement/DefElement/tree/main/defelement/implementations/simplefem.py).
-In this file, we begin by importing functionality from the file
-[core.py](https://github.com/DefElement/DefElement/tree/main/defelement/implementations/core.py):
-
-{{snippet::defelement/implementations/simplefem.py::intro}}
-
-In order to add information about a finite element library to DefElement, we must implement four class methods
-and set the values of four variables in this class.
-
-### `format`
-The first method to implement is `format`.
-The formatting string and parameters set in DefElement's .def files will be passed into this method.
-For simplefem, this method simply returns the format string included in the .def file:
-
-{{snippet::defelement/implementations/simplefem.py::format}}
-
-In the file
-[elements/lagrange.def](https://github.com/DefElement/DefElement/tree/main/elements/lagrange.def)
-the following lines are included in the `implementations` section:
-
-```
-implementations:
- ...
- simplefem:
- equispaced:
- triangle: lagrange_element DEGREEMAP=(k+1)*(k+2)//2
-```
-
-Therefore, the `format` class method will be called for an equispaced Lagrange element on a triangle,
-with `string="lagrange_element"` and `params={}` (note that `DEGREEMAP` uses the syntax for a
-parameter but is not included in `param` due to being a special parameter: we will return to
-`DEGREEMAP` in the next section).
-
-The value returned by `format` will be displayed on the element's page. In the case, the
-class method returns the string `lagrange_element` (which is the function in simplefem used to create
-the element).
-
-### `example_import` and `single_example`
-To generate example code that will be displayed on an element's page, DefElement will use the
-class methods `example_import` and `single_example`. The class method `example_import` returns the
-import statements to include at the start of the example code: this method takes
-the programming language as its only input.
-The class method `single_example`
-returns Python code that will create the element: the inputs to this method are
-the string included in the .def file (`name`);
-the name of the reference cell for this example (`reference`);
-the degree for this example (`degree`);
-the parameters included in the .def file (`params`);
-and the programming language that a snippet should be generated in (`language`).
-The additional inputs `element` and `example` are the DefElement `Element` object and the raw example
-information: these may be needed in some more complex cases.
-
-For simplefem, these class methods are defined as follows:
-
-{{snippet::defelement/implementations/simplefem.py::example}}
-
-The funtion `example_import` gives code to import simplefem. The inputs to class method `single_example`
-will be `name="lagrange_element"`, `reference="triangle"`, and `params={}`. The degree used in
-DefElement (in this case, the [polynomial subdegree](finite-elements.md#The+degree+of+a+finite+element) 1, 2, or 3) will be substituted into the `DEGREEMAP`
-special parameter as `k` before this method is called (so the values 3, 6, and 10 will be passed in).
-In this way, DefElement's notion of degree can be automatically converted to the number of points
-input that simplefem uses.
-
-### `install`
-The class method `install` can be implemented. It takes the programming language as an input
-and returns the bash commands that can be used to install the implementation.
-For simplefem, this is implemented as follows:
-
-{{snippet::defelement/implementations/simplefem.py::install}}
-
-### `version`
-The final class method that must be implemented is `version`, which should return the version
-number of the implementation library. For simplefem, this is implemented as follows:
-
-{{snippet::defelement/implementations/simplefem.py::version}}
-
-When an implementation can be installed from PyPI, the decorator `pypi_name` can be used.
-This decorator will automatically implement the methods `version` and `install`.
-For the Basix library, for example, `pypi_name` is used as
-follows:
-
-{{snippet::defelement/implementations/basix.py::pypi_name}}
-
-### Variables
-Finally, five variables need to be defined:
-
-* `id` gives the id used to identify this implementation (eg in URLs). This should be all lowercase
- and not contain any special characters.
-* `name` gives the name of the implementation as it will appear in text.
-* `url` gives the URL of the git repository of the inplementation.
-* `install` gives the pip command to install the implementation. Note that this can be omitted
- if the `pypi_name` decorator is used.
-* `languages` gives a list of programming languages that this implementation can generate snippets
- for.
-
-Additionallly, the following variables may be needed for some implementations:
-
-* When an implementation supports more than one languages (ie when `len(languages) > 1`),
- the variable `install_language` can be used to set which language should be passed into
- the method `install` to get commands to install the implementation on CI runners.
-
-For simplefem, these variables are set to the following values. In general, it is preferable for
-the install to be done from PyPI rather than via git, but as simplefem is merely an example library
-and has no official release, installing via git is used in this case.
-
-{{snippet::defelement/implementations/simplefem.py::variables}}
-
-### Appearance on element page
-We have now added everything that we need for an implementation to be included on DefElement.
-As simplefem is an example library, it is by default hidden from the
-[Lagrange element page](element::lagrange). If it were included, its section on the page would look
-like this:
-
-
-
Note: This implementation uses an alternative value of degree for this element
-
-
-
-
-## Verification
-As well as giving information about implementations and code snippets for creating elements,
-DefElement is able to [verify](/verification) that the basis functions of elements provided by different
-implementations span the same polynomial spaces. If you want your implementation to be verified, you
-will need to additionally implement the class method `verify` and set the variable `verification` to
-`True`.
-
-The method `verify` takes
-the string included in the .def file (`name`);
-the name of the reference cell for this example (`reference`);
-the degree for this example (`degree`); and
-the parameters included in the .def file (`params`).
-The additional inputs `element` and `example` are the DefElement `Element` object and the raw example
-in case these are needed. The method should return a list of DOFs associated with each entity of each
-dimension (as detailed below) and a function that maps a set of points on the DefElement reference
-cell to a table of values: this table will be a three-dimensional Numpy array with the value
-`table[i][j][k]` giving the `j`th component of the `k`th basis function evalutated at the `i`th
-point.
-
-### `verify` for simplefem
-
-We now look in detail at the implementation of this function for simplefem. We begin with the
-`def` statement defining the method:
-
-{{snippet::defelement/implementations/simplefem.py::verify1}}
-
-The method begins by importing simplefem and numpy: it is important that these are imported inside
-the method so that import errors can be caught when running verification.
-
-{{snippet::defelement/implementations/simplefem.py::verify2}}
-
-The method then uses `getattr` to get the relevant element creation function and creates the element
-(`e`). As only one element is implemented in simplefem, `name` will always be equal to
-`"lagrange_element"`, but this function has been written with a more general library in mind.
-
-{{snippet::defelement/implementations/simplefem.py::verify3}}
-
-We next make lists of which degrees of freedom (DOFs) are associated with each sub-entity: the
-variable `entity_dofs` that is created is a list of lists of lists where
-`entity_dofs[i][j]` is a list of DOFs associated with the `j`th sub-entity of dimension `i`.
-This variable will be one of the two value returned by this function.
-The numbering of DOFs in simplefem is not the same as in DefElement's examples, so
-we do this by looping over the points used to define each basis function and checking
-if they're at a vertex or on an edge. Luckily, the ordering of points on each edge and inside
-the triangle matches the [ordering used by DefElement](/reference_numbering.html#Triangle), so
-these lists do not need to be permuted once created.
-
-{{snippet::defelement/implementations/simplefem.py::verify4}}
-
-Next, we define a function `tabulate` that takes a set of points as an inputs and returns the values
-of all of the basis functions of the element at every point. The points input to this function
-are points on the [reference cell as used by DefElement](/reference_numbering.html#Triangle) and so
-points and function values may need to be mapped if the implementation uses an alternative reference
-cell.
-
-DefElement's reference cell has vertices at (0,0), (1,0) and (0,1), while simplefem's reference cell
-has vertices at (-1,0), (1,0) and (1,0), so we define `mapped_points` to be the points in simplefem's
-reference cell that correspond to the points that are input.
-
-We then loop through each point and each basis function and use `e.evaluate` to evaluate the value
-of the basis function at the point.
-
-{{snippet::defelement/implementations/simplefem.py::verify5}}
-
-Finally, we return `entity_dofs` and the function `tabulate` and set the class variable
-`verification` to `True`:
-{{snippet::defelement/implementations/simplefem.py::verify6}}
-
-{{snippet::defelement/implementations/simplefem.py::verificationvariable}}
-
-### The verification page
-
-As it is an example library, simplefem is hidden from the main verification index pages,
-but can be viewed at [defelement.org/verification/simplefem.html](/verification/simplefem.html).
-
diff --git a/pages/barycentric-dual-grid.md b/pages/barycentric-dual-grid.md
deleted file mode 100644
index 63059c1ce3..0000000000
--- a/pages/barycentric-dual-grid.md
+++ /dev/null
@@ -1,26 +0,0 @@
---
-authors:
- - Scroggs, Matthew W.
---
-
-# Barycentric dual grids
-
-Some elements are defined on a [barycentric dual grid](reference::dual polygon).
-This grid is defined by taking a mesh of triangles:
-
-{{img::mesh-bary0}}
-
-Lines are then added connecting every vertex of each triangle to the midpoint of the opposite
-edge:
-
-{{img::mesh-bary1}}
-
-The cells in the dual grid are then defined as the union of all the triangles adjacent to
-one of the vertices in the original mesh:
-
-{{img::mesh-bary2}}
-
-{{img::mesh-bary3}}
-
-In DefElement, regular polygons centred at the origin are used as reference cells of the
-dual grid.
diff --git a/pages/branding.md b/pages/branding.md
deleted file mode 100644
index 8e77cbc4b6..0000000000
--- a/pages/branding.md
+++ /dev/null
@@ -1,34 +0,0 @@
-# Branding
-
-## Name
-The name of this encyclopedia is DefElement. It should be written with a capital D and capital E, and with no space between Def and Element.
-
-## DefElement logo
-
-The DefElement logo was designed by Matthew Scroggs, and uses letters from the [Varela Round](https://fonts.google.com/specimen/Varela+Round/about) font
-that was designed by Joe Prince.
-The logo can be reused under the same license as the other content on DefElement
-([Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/)).
-More information can be found on the [citing & reuse page](citing.md).
-
-* [DefElement logo 250×250px](/logo-250.png)
-* [DefElement logo 1000×1000px](/logo-1000.png)
-* [DefElement logo 2000×2000px](/logo.png)
-
-## Colours
-The following colours are used on DefElement:
-
-* #FF8800 (DefElement orange)
-* #44AAFF (DefElement blue)
-* #55FF00 (DefElement green)
-* #DD2299 (DefElement pink)
-* #FFFFFF (white)
-* #AAAAAA (grey)
-* #000000 (black)
-
-The background of the top and bottom panels uses these colours:
-
-* #5FB1FF (DefElement blue #2)
-* #84BEFF (DefElement blue #3)
-* #B1D2FF (DefElement blue #4)
-* #DFEBFF (DefElement blue #5)
diff --git a/pages/ciarlet.md b/pages/ciarlet.md
deleted file mode 100644
index 32ea1ac182..0000000000
--- a/pages/ciarlet.md
+++ /dev/null
@@ -1,3 +0,0 @@
---
-redirect: finite-elements.md
---
diff --git a/pages/citing.md b/pages/citing.md
deleted file mode 100644
index 4eda7e279d..0000000000
--- a/pages/citing.md
+++ /dev/null
@@ -1,96 +0,0 @@
-# Reusing & citing
-
-## Reusing content from DefElement
-The text, images, and other content on this website (excluding Font Awesome, which is released under [its own license](https://github.com/DefElement/DefElement/blob/main/files/fontawesome/LICENSE.txt))
-may be reused under the terms of a
-[Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/): this means
-that you can reuse any of the content as long as you attribute DefElement.
-For reuse on a website, you could attribute us by including a link to DefElement;
-for reuse in print, you could attribute us by including "DefElement" somewhere near the image or information;
-for reuse in a paper, you could cite DefElement.
-
-The DefElement logo can be found on the [branding page](branding.md).
-
-## Citing DefElement
-
-The code used to generate this website is available on [Github](https://github.com/DefElement/DefElement)
-under an [MIT license](https://github.com/DefElement/DefElement/blob/main/LICENSE).
-
-On each of the element definition pages, you can find citations for the paper(s) that introduced
-that element. These papers should be cited when using a given element. If you wish to cite this
-website, you can use the following BibTeX:
-
-```
-@misc{defelement,
- AUTHOR = {{{list contributors|bibtex}}},
- TITLE = {{DefElement}: an encyclopedia of finite element definitions},
- YEAR = {{2020--{{date:Y}}}},
- HOWPUBLISHED = {\url{https://defelement.org}},
- NOTE = {[Online; accessed {{date:D-M-Y}}]}
-}
-```
-
-This will create a reference along the lines of:
-
-
-
{{list contributors|citation}}. DefElement: an encyclopedia of finite element definitions, 2020–{{date:Y}}, https://defelement.org [Online; accessed: {{date:D-M-Y}}].
-
-
-### DefElement paper
-
-You may also wish to cite the [DefElement paper](https://dx.doi.org/10.1007/s44207-026-00011-0), which is currently available as a preprint on arΧiv.
-To cite this, you can use the following BibTeX:
-
-```
-@article{2026-defelement,
- AUTHOR = {Scroggs, Matthew W.
- and Brubeck, Pablo D.
- and Dean, Joseph P.
- and Dokken, J{\o}rgen S.
- and Marsden, India},
- TITLE = {{DefElement:} an encyclopedia of finite element definitions},
- YEAR = {2026},
- JOURNAL = {Computational Science and Engineering},
- VOLUME = {3},
- NUMBER = {2},
- PAGES = {{1--31}},
- DOI = {10.1007/s44207-026-00011-0}
-}
-```
-
-This will create a reference along the lines of:
-
-
-
M. W. Scroggs, P. D. Brubeck, J. P. Dean, J. S. Dokken, I. Marsden. DefElement: an encyclopedia of finite element definitions, 2026, Computational Science and Engineering 3(2), 1–31, https://doi.org/10.1007/s44207-026-00011-0.
-
-
-## DefElement poster
-The [DefElement poster](https://doi.org/10.6084/m9.figshare.23294939.v1) was first presented at [FEniCS 2023](https://fenicsproject.org/fenics-2023/).
-
-If you'd like a copy of the poster, you can download
-[the A0 poster](/pdfs/poster-a0.pdf),
-[the A0 poster (with bleed for printing)](/pdfs/poster-a0-bleed.pdf),
-[the A1 poster](/pdfs/poster-a1.pdf),
-[the A1 poster (with bleed for printing)](/pdfs/poster-a1-bleed.pdf),
-[the A4 poster](/pdfs/poster-a4.pdf), or
-[the A4 poster (with bleed for printing)](/pdfs/poster-a4-bleed.pdf).
-These are all available under the same [Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/)
-as the rest of DefElement.
-
-If you wich to cite the poster, you can use the following BibTeX:
-
-```
-@misc{defelement-poster,
- AUTHOR = {Scroggs, Matthew W.},
- TITLE = {{DefElement}: an encyclopedia of finite element definitions},
- YEAR = {2023},
- HOWPUBLISHED = {Poster presented at FEniCS 2023, Cagliari, Italy},
- DOI = {10.6084/m9.figshare.23294939.v1},
-}
-```
-
-This will create a reference along the lines of:
-
-
diff --git a/pages/code-of-conduct.md b/pages/code-of-conduct.md
deleted file mode 100644
index 75e3273bda..0000000000
--- a/pages/code-of-conduct.md
+++ /dev/null
@@ -1 +0,0 @@
-{{CODE_OF_CONDUCT.md}}
diff --git a/pages/contributing.md b/pages/contributing.md
deleted file mode 100644
index 8f6fed0ad4..0000000000
--- a/pages/contributing.md
+++ /dev/null
@@ -1 +0,0 @@
-{{CONTRIBUTING.md}}
diff --git a/pages/contributors.md b/pages/contributors.md
deleted file mode 100644
index 7be8c73cb6..0000000000
--- a/pages/contributors.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# DefElement contributors
-
-Details of the contributions made by everyone can be found [on Github](https://github.com/DefElement/DefElement/graphs/contributors).
-
-{{list contributors}}
diff --git a/pages/de-rham.md b/pages/de-rham.md
deleted file mode 100644
index 1cd874362c..0000000000
--- a/pages/de-rham.md
+++ /dev/null
@@ -1,58 +0,0 @@
---
-authors:
- - Scroggs, Matthew W.
- - Nobre, Nuno
---
-
-# De Rham element families
-The following relationship is the de Rham complex in 3D:
-$$
-H^1
-\xrightarrow{\nabla}
-\textbf{H}(\text{curl})
-\xrightarrow{\nabla\times}
-\textbf{H}(\text{div})
-\xrightarrow{\nabla\cdot}
-L^2
-$$
-We say this sequence is exact since the range (or image) of each of the
-differential operators coincides with the null space (or kernel) of the next
-operator in the sequence. We also note the last map is a surjection.
-
-A set of four finite elements \(\mathcal{V}_0\) to \(\mathcal{V}_3\) forms
-a discrete de Rham complex if the following commutative diagram holds,
-where \(I_0\) to \(I_3\) are interpolations into \(\mathcal{V}_0\) to \(\mathcal{V}_3\).
-(The commutative diagram holds if following different arrow combinations to the same destination will give the same result.)
-$$
-\begin{array}{ccccccc}
-H^1
-&\xrightarrow{\nabla}
-&\textbf{H}(\text{curl})
-&\xrightarrow{\nabla\times}
-&\textbf{H}(\text{div})
-&\xrightarrow{\nabla\cdot}
-&L^2\\
-\hphantom{\small I_0}\big\downarrow {\small I_0}&&
-\hphantom{\small I_1}\big\downarrow {\small I_1}&&
-\hphantom{\small I_2}\big\downarrow {\small I_2}&&
-\hphantom{\small I_3}\big\downarrow {\small I_3}\\
-\mathcal{V}_0
-&\xrightarrow{\nabla}
-&\mathcal{V}_1
-&\xrightarrow{\nabla\times}
-&\mathcal{V}_2
-&\xrightarrow{\nabla\cdot}
-&\mathcal{V}_3
-\end{array}
-$$
-Sequences of finite element spaces forming discrete de Rham complexes are, in
-general, not exact. However, it is certainly still the case that the range of
-each of the differential operators is contained in (but is not necessarily
-coincident with) the null space of the next operator in the sequence.
-
-You can view families of elements that form discrete de Rham complexes on the [families page](index::families).
-On DefElement, two naming conventions for elements in a de Rham complex are used.
-The first of these is the exterior calculus convention: this is the notation used in the
-Periodic table of the finite elements
-.
-The second is the Cockburn–Fu convention: this gives the names used for element families in Cockburn and Fu's 2017 paper.
diff --git a/pages/finite-elements.md b/pages/finite-elements.md
deleted file mode 100644
index 76036fd1a6..0000000000
--- a/pages/finite-elements.md
+++ /dev/null
@@ -1,283 +0,0 @@
---
-authors:
- - Scroggs, Matthew W.
---
-
-# How to define a finite element
-This page describes how finite elements are defined in the DefElement database.
-Much of the information on this page in written up in more detail in the DefElement paper {{citation::defelement_paper}}.
-
-## Reference cells
-The reference cell on which an element is defined is arbitrary, and there are multiple different standard cells that are used. In DefElement, we use the following reference cells,
-with sub-entities numbered as shown on the [reference numbering page](reference_numbering.md).
-
-* The reference interval is \(\left\{x\,\middle|\,0\leqslant x\leqslant 1\right\}\).
-* The reference triangle is \(\left\{(x,y)\,\middle|\,0\leqslant x,\,0\leqslant y,\,x+y\leqslant1\right\}\).
-* The reference quadrilateral is \(\left\{(x,y)\,\middle|\,0\leqslant x\leqslant1,\,0\leqslant y\leqslant1\right\}\).
-* The reference tetrahedron is \(\left\{(x,y,z)\,\middle|\,0\leqslant x,\,0\leqslant y,\,0\leqslant z,\,x+y+z\leqslant1\right\}\).
-* The reference hexahedron is \(\left\{(x,y,z)\,\middle|\,0\leqslant x\leqslant1,\,0\leqslant y\leqslant1,\,0\leqslant z\leqslant1\right\}\).
-* The reference prism is \(\left\{(x,y,z)\,\middle|\,0\leqslant x,\,0\leqslant y,\,x+y\leqslant1,\,0\leqslant z\leqslant1\right\}\).
-* The reference pyramid is \(\left\{(x,y,z)\,\middle|\,0\leqslant x,\,0\leqslant y,\,0\leqslant z\leqslant1,\,x+z\leqslant1,\,y+z\leqslant1\right\}\).
-
-## Cell sub-entities
-Throughout this website, the sub-entities of a cell \({{symbols.reference}}\subset\mathbb{R}^d\) will be referred to as described here.
-
-The topological dimension \(d\) is the dimension of the reference cell itself.
-(When using the finite element method, the topological dimensions may differ from the
-geometric dimension \(d_g\): for example, when meshing a 2D manifold in 3D space, the
-topological and geometric dimensions are 2 and 3 (respectively).)
-
-The sub-entities of a cell of dimension 0, 1, 2, and 3 are called
-vertices, edges, faces, and volumes (respectively).
-The codimension of an entity is given by subtracting the dimension of the entity from the
-topological dimension \(d\) of the cell. Entities of codimension 1, 2, and 3 are
-called facets, ridges and peaks (respectively).
-For \(k>3\), we do not introduce specific names for \(k\)-dimensional and \(k\)-codimensional sub-entities.
-
-The names defined here are summarised for cells of topological dimension 0 to 4 below.
-
-
-
-
-
Topological dimension
Entities by dimension
-
0
1
2
3
4
-
-
0 (a vertex)
the cell
-
-
-
-
-
1 (an interval)
points / facets
the cell
-
-
-
-
2 (a polygon)
points / ridges
edges / facets
the cell
-
-
-
3 (a polyhedron)
points / peaks
edges / ridges
faces / facets
the cell
-
-
4
points
edges / peaks
faces / ridges
volumes / facets
the cell
-
-
-
-
-
-
-
Topological dimension
Entities by codimension
-
0
1
2
3
4
-
-
0 (a vertex)
the cell
-
-
-
-
-
1 (an interval)
the cell
points / facets
-
-
-
-
2 (a polygon)
the cell
edges / facets
points / ridges
-
-
-
3 (a polyhedron)
the cell
faces / facets
edges / ridges
points / peaks
-
-
4
the cell
volumes / facets
faces / ridges
edges / peaks
points
-
-
-
-## Finite elements
-In the most general terms, a finite element on a cell \({{symbols.cell}}\) is defined as a triple \(({{symbols.cell}},{{symbols.polyset}},{{symbols.dual_basis}})\),
-where
-
-* \({{symbols.cell}}\subset\mathbb{R}^{d_g}\) a cell in a mesh;
-* \({{symbols.polyset}}\) is a finite dimensional space on \({{symbols.cell}}\) of dimension \(n\), usually a space of polynomials;
-* \({{symbols.dual_basis}}=\{{{symbols.functional}}_0,...,{{symbols.functional}}_{n-1}\}\) is a basis of the dual space \({{symbols.polyset}}^*=\{f:{{symbols.polyset}}\to\mathbb{R}|f\text{ is linear}\}\). Each functional \({{symbols.functional}}_i\) is associated with a sub-entity of \({{symbols.cell}}\).
-
-The basis functions \(\{{{symbols.basis_function}}_0,...,{{symbols.basis_function}}_{n-1}\}\)
-of the finite element space are defined by
-
-\[{{symbols.functional}}_i({{symbols.basis_function}}_j) = \begin{cases}1&i=j\\0&i\not=j\end{cases}\]
-
-Note that on pyramid cells, a space of rationomials (rational polynomials) is typically used for \({{symbols.polyset}}\). For all other cells, a space of polynomials is typically used.
-
-The majority of the elements included in DefElement are defined as reference-mapped elements. Reference-mapped elements are defined on
-a reference cell then mapped to physical cells using a push forward map. On a reference cell, a reference-mapped element is defined by a
-triple \(({{symbols.reference}},{{symbols.polyset}},{{symbols.dual_basis}})\), where
-
-* \({{symbols.reference}}\subset\mathbb{R}^d\) is the reference cell, usually a polygon or polyhedron;
-* \({{symbols.polyset}}\) is a finite dimensional space on \({{symbols.reference}}\) of dimension \(n\), usually a space of polynomials;
-* \({{symbols.dual_basis}}=\{{{symbols.functional}}_0,...,{{symbols.functional}}_{n-1}\}\) is a basis of the dual space \({{symbols.polyset}}^*=\{f:{{symbols.polyset}}\to\mathbb{R}|f\text{ is linear}\}\). Each functional \({{symbols.functional}}_i\) is associated with a sub-entity of the reference cell \({{symbols.reference}}\).
-
-The correct continuity is enforced by ensuring that the same global degree-of-freedom is associated with corresponding functionals on the shared sub-entities of neighbouring cells.
-
-Reference-mapped finite elements are often referred to as Ciarlet elements, do to their definition first appearing and
-a set of lecture notes
-and a later book
-by Philippe Ciarlet.
-
-### Example: Order 1 Lagrange space on a triangle
-An order 1 [Lagrange space](element::lagrange) on a triangle is defined by:
-
-* \({{symbols.reference}}\) is a triangle with vertices at \((0,0)\), \((1,0)\) and \((0,1)\);
-* \({{symbols.polyset}}=\operatorname{span}\{1, x, y\}\);
-* \({{symbols.dual_basis}}=\{{{symbols.functional}}_0,{{symbols.functional}}_1,{{symbols.functional}}_2\}\).
-
-The functionals \({{symbols.functional}}_0\) to \({{symbols.functional}}_2\) are defined as
-point evaluations at the three vertices of the triangle:
-
-\[{{symbols.functional}}_0:v\mapsto v(0,0)\]
-\[{{symbols.functional}}_1:v\mapsto v(1,0)\]
-\[{{symbols.functional}}_2:v\mapsto v(0,1)\]
-
-It follows from these definitions that the basis functions of the finite element spaces are
-linear functions that are equal 1 to at one of the vertices, and equal to 0 at the other two.
-These are:
-
-\[{{symbols.basis_function}}_0(x,y)=1-x-y\]
-\[{{symbols.basis_function}}_1(x,y)=x\]
-\[{{symbols.basis_function}}_2(x,y)=y\]
-
-{{plot::triangle,Lagrange,1}}
-
-## Integral moments
-It is common to use integral moment functionals when defining finite elements. Given a mesh
-entity \(e\) and a finite element space \((e,{{symbols.polyset}}_e,{{symbols.dual_basis}}_e)\) defined on \(e\),
-the integral moment functionals \({{symbols.functional}}_1,...,{{symbols.functional}}_{n_e}\) are defined by
-\[{{symbols.functional}}_i:v\mapsto \int_e v{{symbols.basis_function}}_i,\]
-where \({{symbols.basis_function}}_1,...,{{symbols.basis_function}}_{n_e}\) are the basis functions of the finite
-element space on \(e\).
-
-For vector-valued spaces, integral moment functional can be defined using a vector-valued space
-on \(e\) and taking the dot product inside the integral,
-\[{{symbols.functional}}_i:\boldsymbol{v}\mapsto \int_e \boldsymbol{v}\cdot{{symbols.vector_basis_function}}_i.\]
-Alternatively, an integral moment can
-be taken with a scalar-valued space by taking the dot product with a fixed vector \(\boldsymbol{a}\),
-\[{{symbols.functional}}_i:\boldsymbol{v}\mapsto \int_e \boldsymbol{v}\cdot\boldsymbol{a}\,{{symbols.basis_function}}_i.\]
-Typically, \(\boldsymbol{a}\) will be tangent to an edge, normal to a facet, or a unit vector in one of
-the coordinate directions.
-
-### Example: Order 1 Nédélec (second kind) space on a triangle
-The functionals that define an order 1 [Nédélec (second kind) space](element::nedelec2)
-on a triangle
-are tangential integral moments with order 1 Lagrange spaces on the edges of the triangle.
-For example, the two functionals on the edge \(e_0\) of the triangle between \((0,0)\) and \((1,0)\) are
-\[{{symbols.functional}}_0:\boldsymbol{v}\to\int_{e_0}\boldsymbol{v}\cdot\left(\begin{array}{c}0\\1\end{array}\right)(1-s_0),\]
-\[{{symbols.functional}}_1:\boldsymbol{v}\to\int_{e_0}\boldsymbol{v}\cdot\left(\begin{array}{c}0\\1\end{array}\right)s_0,\]
-where \(s_0\) varies from 0 (at \((0,0)\)) to 1 (at \((1,0)\)) along \(e_0\).
-
-The basis functions of this space are:
-{{plot::triangle,N2curl,1}}
-
-## Mapping finite elements
-In order to maintain desired properties when mapping finite elements from a reference
-cell to an actual mesh, an appropriate mapping must be defined.
-
-
-For elements with a mixture of functional types, a more complex approach is required.
-{{citation::kirby_mapping}}
-
-Let \({{symbols.geometry_map}}\) be a transformation that maps the reference cell to a cell in the mesh,
-and let \(\boldsymbol{x}\) be a point in the cell.
-
-The Jacobian, \({{symbols.jacobian}}\), of the transformation \({{symbols.geometry_map}}\) is:
-
-* \(\displaystyle\frac{\mathrm{d}F}{\mathrm{d}x}\) for 1D reference cells (with 1D physical cells),
-* \(\displaystyle\left(
- \begin{array}{cc}
- \frac{\partial F_1}{\partial x}&\frac{\partial F_1}{\partial y}\\
- \frac{\partial F_2}{\partial x}&\frac{\partial F_2}{\partial y}
- \end{array}
- \right)\) for 2D reference cells (with 2D physical cells),
-* \(\displaystyle\left(
- \begin{array}{ccc}
- \frac{\partial F_1}{\partial x}&\frac{\partial F_1}{\partial y}&\frac{\partial F_1}{\partial z}\\
- \frac{\partial F_2}{\partial x}&\frac{\partial F_2}{\partial y}&\frac{\partial F_2}{\partial z}\\
- \frac{\partial F_3}{\partial x}&\frac{\partial F_3}{\partial y}&\frac{\partial F_3}{\partial z}
- \end{array}
- \right)\) for 3D reference cells (with 3D physical cells).
-
-If the dimensions of the reference and physical cells are not equal (eg for a surface mesh of 2D cells embedded in 3D)
-then \({{symbols.jacobian}}\) will be a rectangular matrix. In this case then in the definitions below,
-the determinant \(\det({{symbols.jacobian}})\) should be replaced by \(\sqrt{\det({{symbols.jacobian}}^T{{symbols.jacobian}})}\)
-and the inverse matrix \({{symbols.jacobian}}^{-1}\) should be replaced by the Moore–Penrose pseudoinverse matrix
-\({{symbols.jacobian}}^\dagger=\left({{symbols.jacobian}}^T{{symbols.jacobian}}\right)^{-1}{{symbols.jacobian}}^T\) that satisfies \({{symbols.jacobian}}^\dagger{{symbols.jacobian}} = \mathbf{I}\).
-
-### Scalar-valued basis functions
-The identity mapping—used to map scalar basis functions, \({{symbols.basis_function}}\)—is defined by
-\[\left({{symbols.mapping}}^\text{id}{{symbols.basis_function}}\right)(\boldsymbol{x})
-:={{symbols.basis_function}}({{symbols.geometry_map}}^{-1}(\boldsymbol{x})).\]
-The term \({{symbols.geometry_map}}^{-1}(\boldsymbol{x})\) is the point on the reference cell corresponding to the point \(\boldsymbol{x}\), so this mapping
-maps a value of the function on the reference to the same value at the corresponding point.
-
-The L2 Piola mapping—used to map scalar discontinuous elements, \({{symbols.basis_function}}\)—is defined by
-\[\left({{symbols.mapping}}^\text{L2}{{symbols.basis_function}}\right)(\boldsymbol{x})
-:=\frac1{\det {{symbols.jacobian}}}{{symbols.basis_function}}({{symbols.geometry_map}}^{-1}(\boldsymbol{x})).\]
-
-### Vector-valued basis functions
-For vector-valued basis functions, \({{symbols.vector_basis_function}}\), the
-covariant Piola (\({{symbols.mapping}}^\text{curl}\)) and
-contravariant Piola (\({{symbols.mapping}}^\text{div}\)) mappings are defined:
-\[\left({{symbols.mapping}}^\text{curl}{{symbols.vector_basis_function}}\right)(\boldsymbol{x})
-:={{symbols.jacobian}}^{-T}{{symbols.vector_basis_function}}({{symbols.geometry_map}}^{-1}(\boldsymbol{x}))\]
-\[\left({{symbols.mapping}}^\text{div}{{symbols.vector_basis_function}}\right)(\boldsymbol{x})
-:=\frac1{\det {{symbols.jacobian}}}{{symbols.jacobian}}{{symbols.vector_basis_function}}({{symbols.geometry_map}}^{-1}(\boldsymbol{x}))\]
-The covariant Piola mapping preserves the tangential component of basis functions on edges and facets,
-and are typically used to map H(curl) elements.
-The contravariant Piola mapping preserves the normal component of basis functions on facets,
-and are typically used to map H(div) elements.
-
-### Matrix-valued basis functions
-For matrix-valued basis functions, \({{symbols.matrix_basis_function}}\), the
-double covariant Piola (\({{symbols.mapping}}^\text{curl curl}\)),
-double contravariant Piola (\({{symbols.mapping}}^\text{div div}\)) and
-covariant-contravariant Piola (\({{symbols.mapping}}^\text{curl div}\))
-mappings are defined:
-\[\left({{symbols.mapping}}^\text{curl curl}{{symbols.matrix_basis_function}}\right)(\boldsymbol{x})
-:={{symbols.jacobian}}^{-T}{{symbols.matrix_basis_function}}({{symbols.geometry_map}}^{-1}(\boldsymbol{x})){{symbols.jacobian}}^{-1}\]
-\[\left({{symbols.mapping}}^\text{div div}{{symbols.matrix_basis_function}}\right)(\boldsymbol{x})
-:=\frac1{\left(\det {{symbols.jacobian}}\right)^2}{{symbols.jacobian}}{{symbols.matrix_basis_function}}({{symbols.geometry_map}}^{-1}(\boldsymbol{x})){{symbols.jacobian}}^T\]
-\[\left({{symbols.mapping}}^\text{curl div}{{symbols.matrix_basis_function}}\right)(\boldsymbol{x})
-:=\frac1{\left(\det {{symbols.jacobian}}\right)}{{symbols.jacobian}}^{-T}{{symbols.matrix_basis_function}}({{symbols.geometry_map}}^{-1}(\boldsymbol{x})){{symbols.jacobian}}^T\]
-
-## Variants of finite elements
-For many elements, there are a number of different choices that could be made for the functionals
-in \({{symbols.dual_basis}}\) that define the element that give rise to an element with the same
-key properties. For example, when defining a [Lagrange element](element::lagrange) on a triangle, there are many possible
-choices for where exactly to locate the point evaluation functionals.
-We refer to a pair of elements as variants of each other if:
-
-* They are defined on the same reference cell \({{symbols.reference}}\);
-* They are defined using the same space \({{symbols.polyset}}\);
-* The set of functionals in \({{symbols.dual_basis}}\) associated with each sub-entity of the
- cell for the two elements are equivalent.
-
-Commonly used variants of elements are shown on each element's page.
-
-## The degree of a finite element
-There are a few different ways to describe the degree of a finite element.
-These are defined by:
-
-* The polynomial subdegree is the degree of the highest degree complete polynomial space that is a subspace of this element's polynomial space.
-* The polynomial superdegree is the degree of the lowest degree complete polynomial space that is a superspace of this element's polynomial space.
-* The Lagrange subdegree is the degree of the highest degree natural space that is a subspace of this element's polynomial space.
-* The Lagrange superdegree is the degree of the lowest degree natural space that is a superspace of this element's polynomial space.
-
-The natural polynomial (or rationomial for pyramids) spaces of degree \(k\) on an interval, triangle, quadrilateral,
-tetrahedron, hexahedron, triangular prism and square-based pyramid are defined by
-
-\[\mathbb{P}^{\text{interval}}_k=\operatorname{span}\left\{x^{p_0}\,\middle|\,p_0\in\mathbb{N},\,p_0\leqslant k\right\},\]
-\[\mathbb{P}^{\text{triangle}}_k=\operatorname{span}\left\{x^{p_0}y^{p_1}\,\middle|\,p_0,p_1\in\mathbb{N}_0,\,p_0+p_1\leqslant k\right\},\]
-\[\mathbb{P}^{\text{quadrilateral}}_k=\operatorname{span}\left\{x^{p_0}y^{p_1}\,\middle|\,p_0,p_1\in\mathbb{N}_0,\,p_0\leqslant k,\,p_1\leqslant k\right\},\]
-\[\mathbb{P}^{\text{tetrahedron}}_k=\operatorname{span}\left\{x^{p_0}y^{p_1}z^{p_2}\,\middle|\,p_0,p_1,p_2\in\mathbb{N}_0,\,p_0+p_1+p_2\leqslant k\right\},\]
-\[\mathbb{P}^{\text{hexahedron}}_k=\operatorname{span}\left\{x^{p_0}y^{p_1}z^{p_2}\,\middle|\,p_0,p_1,p_2\in\mathbb{N}_0,\,p_0\leqslant k,\,p_1\leqslant k,\,p_2\leqslant k\right\},\]
-\[\mathbb{P}^{\text{prism}}_k=\operatorname{span}\left\{x^{p_0}y^{p_1}z^{p_2}\,\middle|\,p_0,p_1,p_2\in\mathbb{N}_0,\,p_0+p_1\leqslant k,\,p_2\leqslant k\right\},\]
-\[\mathbb{P}^{\text{pyramid}}_k=\operatorname{span}\left\{\frac{x^{p_0}y^{p_1}z^{p_2}}{(1-z)^{p_0+p_1}}\,\middle|\,p_0,p_1,p_2\in\mathbb{N}_0,\,p_0\leqslant k,\,p_1\leqslant k,\,p_2\leqslant k\right\}.\]
-
-For non-pyramid cell types, these coincide with the polynomial space of a [Lagrange element](element::lagrange), hence the names Lagrange subdegree and Lagrange superdegree
-
-On each element's page, the values of these is shown, as well as information about which one is used as the canonical degree of that element.
-In general, the polynomial subdegree is used to index every element whenever possible; the numbering on DefElement therefore differs from that used
-in some implementations (most notably FEniCS, where the Lagrange superdegree is used to index the majority of elements).
-
-## Notation
-Throughout this website, the notation given here in this section is used.
-
-
A basis function of a degree 0 [Raviart–Thomas space](element::raviart-thomas) on a triangle
-
{{plot::quadrilateral,Q,2::3}}
-
A basis function of a degree 2 [Lagrange space](element::lagrange) on a quadrilateral
-
{{plot::tetrahedron,N1curl,0::4}}
-
A basis function of a degree 0 [Nédélec (first kind) space](element::nedelec1) on a tetrahedron
-
{{plot::hexahedron,Scurl,1::13}}
-
A basis function of a degree 1 [serendipity H(curl) space](element::scurl) on a hexahedron
-
-
-Welcome to DefElement: an encyclopedia of finite element definitions.
-
-This website contains a collection of definitions of finite elements,
-including commonly used elements such as
-[Lagrange](element::lagrange),
-[Raviart–Thomas](element::raviart-thomas),
-[Nédélec (first kind)](element::nedelec1),
-and
-[Nédélec (second kind)](element::nedelec2)
-elements,
-and more exotic elements such as
-[serendipity](element::serendipity),
-[Hermite](element::hermite),
-[P1-iso-P2](element::p1-iso-p2),
-and
-[Regge](element::regge)
-elements.
-DefElement currently contains the definition of {{number-of-elements}} elements.
-
-You can:
-
-* [view the full alphabetical list of elements](index::all)
-* [view the elements by category](index::categories)
-* [view the elements by reference cell](index::references)
-* [view the elements that form complexes](index::families)
-* [view the elements by available implementations](index::implementations)
-* [view recently added/updated elements](index::recent)
-* [view a list of all pages on DefElement](/sitemap.html)
-
-## The finite element method
-The finite element method is a numerical method that involves discretising a problem using a finite
-dimensional function space. These function spaces are commonly defined using a finite element
-on a reference cell to derive basis functions for the space. This website contains a collection
-of finite elements, and examples of the basis functions they define.
-
-Following the Ciarlet definition of a finite element, the elements on this website
-are defined using a reference cell, a polynomial space, and a set of functionals. Each element's
-page describes how these are defined for that element, and gives examples of these and the basis
-functions they lead to for a selection of low degree spaces.
-
-You can read a detailed description of how the finite element definitions can be understood
-on the [how to understand a finite element page](finite-elements.md).
-
-## Implementations of finite elements
-There are a large number of libraries that implement finite elements. On each element's page,
-code snippets are provided that can create the element in a selection of these libraries.
-
-For many libraries, DefElement performs verification to check that the implementation of
-each element in the library matches the element's definition. The results of the most
-recent verification run can be found on the [verification page](/verification).
-
-## Contributing to DefElement
-If you find an error or inaccuracy in a DefElement entry, please open
-[an issue on Github](https://github.com/DefElement/DefElement/issues).
-You can also open an issue to suggest a new element that should be added to the database.
-
-Alternatively, you could fork the [DefElement Github repo](https://github.com/DefElement/DefElement),
-make the changes yourself, and open a pull request. You can find more information about adding
-an element to DefElement on the [contributing page](contributing.md).
-
-The functional information and examples on the element pages are generated using
-[Symfem](https://github.com/mscroggs/symfem), a symbolic finite element definition library.
-Before adding an element to DefElement, it should first be implemented in Symfem.
-
-A list of everyone who has contributed to DefElement can be found on the [contributors page](contributors.md).
-
-## Licensing and reuse
-All the information and images on DefElement are licensed under a
-[Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/): this means
-that you can reuse them as long as you attribute DefElement.
-Full details of the licenses and attributions can be found on the [citing page](citing.md).
diff --git a/pages/reference_numbering.md b/pages/reference_numbering.md
deleted file mode 100644
index bcd915ce8c..0000000000
--- a/pages/reference_numbering.md
+++ /dev/null
@@ -1,20 +0,0 @@
---
-title: Reference cell numbering
---
-This page illustrates the entity numbering used for each reference cell. In general, the following conventions are used when numbering cells on DefElement.
-
-### Convention 1: vertex numbering
-
-If \(a=(a_0,...,a_{d-1})\) and \(b=(a_0,...,b_{d-1})\) are two vertices of a reference cell then the index of vertex
-\(a\) is less than the index of vertex \(b\) if and only if \((a_{d-1},...,a_0)<(b_{d-1},...,b_0)\) (where the meaning of \(<\) is as in Python for tuples).
-
-Note that dual cells are treated as a special case, with the vertices numbered in an anticlockwise order.
-
-### Convention 2: sub-entity numbering
-
-Let \(a\) and \(b\) be two sub-entities of a reference cell, and let \(v_a\) and \(v_b\) be sets containing the vertices of \(a\) and \(b\).
-The index of sub-entity \(a\) is less that the index of sub-entity \(b\) if and only if
-\(v_a < v_b\) (where again the meaning of \(<\) is as in Python for tuples).
-
-
-{{REFERENCE_CELL_NUMBERING}}
diff --git a/pages/style-guide.md b/pages/style-guide.md
deleted file mode 100644
index ef04dd6832..0000000000
--- a/pages/style-guide.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# Style guide
-All the content on DefElement aims to conform to this style guide. This keeps it clear and consistent.
-
-We use standard British spelling in the content on DefElement.
-
-In general, we follow the [Chalkdust style guide](https://chalkdustmagazine.com/style-guide/)
-(which follows the [Guardian style guide](https://www.theguardian.com/guardian-observer-style-guide-a)
-with some additional guidance, mostly related to the style of equations). The remainder of this
-page lists style guidance not covered in the Chalkdust style guide.
-
-### DefElement
-This website is called DefElement, with a capital D and capital E, with no space between Def and Element.
-More detail on referring the site itself can be found on the [branding page](branding.md).
-
-### Notation
-The notation used on DefElement can be found [in this list](finite-elements.md#Notation).
-
-### Reference cell
-The cell \({{symbols.reference}}\) on which an element is defined should always be referred to as the reference
-cell and never the reference element to avoid confusion with the finite element.
-
-### References
-References to papers, books, etc should follow the following style guidance:
-
-* Author names should include the full first name, middle initial(s) followed by full stop(s), then
- surname. In .def files, they are written as Surname, First Name Initials (eg
- "Scroggs, Matthew W.")
-* Paper titles should use sentence case (like this)
-* Journal and Book Names Should Use Title Case (Like This)
-
-### Sobolev spaces
-When written in text, the spaces H(div) and H(curl) should be written as text, and not in math-mode.
-
-### Sub-entities
-Hypenated, not subentities.
diff --git a/people/bot.png b/people/bot.png
deleted file mode 100644
index 1f7f6aaf60..0000000000
Binary files a/people/bot.png and /dev/null differ
diff --git a/people/brubeck.jpeg b/people/brubeck.jpeg
deleted file mode 100644
index 9dc31f54f8..0000000000
Binary files a/people/brubeck.jpeg and /dev/null differ
diff --git a/people/dean.png b/people/dean.png
deleted file mode 100644
index 27b33d203f..0000000000
Binary files a/people/dean.png and /dev/null differ
diff --git a/people/dokken.jpg b/people/dokken.jpg
deleted file mode 100644
index a9f88ed270..0000000000
Binary files a/people/dokken.jpg and /dev/null differ
diff --git a/people/m-scroggs.jpg b/people/m-scroggs.jpg
deleted file mode 100755
index 6b6c86e518..0000000000
Binary files a/people/m-scroggs.jpg and /dev/null differ
diff --git a/people/marsden.jpeg b/people/marsden.jpeg
deleted file mode 100644
index 0776a687da..0000000000
Binary files a/people/marsden.jpeg and /dev/null differ
diff --git a/people/nobre.jpeg b/people/nobre.jpeg
deleted file mode 100644
index 0c2a4ea564..0000000000
Binary files a/people/nobre.jpeg and /dev/null differ
diff --git a/pyproject.toml b/pyproject.toml
deleted file mode 100644
index 1f7f0a9881..0000000000
--- a/pyproject.toml
+++ /dev/null
@@ -1,42 +0,0 @@
-[build-system]
-build-backend = "flit_core.buildapi"
-requires = ["flit_core >=3.8.0,<4"]
-
-[project]
-name = "defelement"
-description = "an encyclopedia of finite element definitions"
-version = "2026.07"
-readme = "README.md"
-requires-python = ">=3.11,<3.15"
-license = "MIT"
-authors = [
- { name = "Matthew Scroggs", email = "defelement@mscroggs.co.uk" },
- { name = "other contributors to DefElement" }
-]
-dependencies = [
- "cairosvg==2.9",
- "symfem[optional]==2025.12.0",
- "numpy==2.5",
- "sympy==1.14.0",
- "PyGithub==2.9",
- "pytz==2026.3",
- "pyyaml==6.0.3",
- "website-build-tools==0.1.14"
-]
-packages = ["defelement", "defelement.implementations"]
-
-[project.optional-dependencies]
-style = ["ruff", "mypy", "pydocstyle"]
-
-[tool.ruff]
-line-length = 100
-indent-width = 4
-
-[tool.ruff.lint.per-file-ignores]
-"__init__.py" = ["F401"]
-
-[tool.ruff.lint.pydocstyle]
-convention = "google"
-
-[tool.mypy]
-ignore_missing_imports = true
diff --git a/templates/intro.html b/templates/intro.html
deleted file mode 100644
index 43bb47c96a..0000000000
--- a/templates/intro.html
+++ /dev/null
@@ -1,53 +0,0 @@
-
-
-
-
-
-
-
-{{pagetitle | }}DefElement
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/test/test_continuity.py b/test/test_continuity.py
deleted file mode 100644
index acf2240e9e..0000000000
--- a/test/test_continuity.py
+++ /dev/null
@@ -1,55 +0,0 @@
-import os
-
-import pytest
-import yaml
-
-element_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../elements")
-
-inputs = []
-for i in os.listdir(element_path):
- if i.endswith(".def"):
- with open(os.path.join(element_path, i)) as f:
- data = yaml.load(f, Loader=yaml.FullLoader)
- inputs += [(i, c) for c in data["reference-cells"]]
-
-
-@pytest.mark.parametrize("file, cellname", inputs)
-def test_sequence(file, cellname):
- with open(os.path.join(element_path, file)) as f:
- data = yaml.load(f, Loader=yaml.FullLoader)
-
- if "mapping" not in data:
- pytest.skip()
- if "sobolev" not in data:
- pytest.skip()
-
- m = data["mapping"]
- c = data["sobolev"]
- if isinstance(c, dict):
- c = list(c.values())
- else:
- c = [c]
-
- if m == "identity":
- for i in c:
- assert i in ["L2", "H1", "H2", "H3"]
- elif m == "L2 Piola":
- for i in c:
- assert i in ["L2"]
- elif m == "covariant Piola":
- for i in c:
- assert i == "H(curl)"
- elif m == "contravariant Piola":
- for i in c:
- assert i in ["H(div)", "H1(div)", "H1"]
- elif m == "double covariant Piola":
- for i in c:
- assert i == "H(curl curl)"
- elif m == "double contravariant Piola":
- for i in c:
- assert i == "H(div div)"
- elif m == "covariant-contravariant Piola":
- for i in c:
- assert i == "H(curl div)"
- else:
- pytest.skip(f"Non-standard mapping: {m}")
diff --git a/test/test_element_files.py b/test/test_element_files.py
deleted file mode 100644
index 6ac1f00af8..0000000000
--- a/test/test_element_files.py
+++ /dev/null
@@ -1,62 +0,0 @@
-import os
-
-import pytest
-import yaml
-
-dir_path = os.path.dirname(os.path.realpath(__file__))
-element_path = os.path.join(dir_path, "../elements")
-
-
-def parse_contributing_page():
- with open(os.path.join(dir_path, "../CONTRIBUTING.md")) as f:
- table = f.read().split("
", 1)[1].split("
")[0]
- if "" in table:
- table = table.split("")[1]
- table = table.strip()
-
- docs = {"req": [], "opt": [], "all": []}
- for line in table.split("\n"):
- name = line.split("`")[1].replace("‑", "-")
- docs["all"].append(name)
- if "{{tick}}" in line:
- docs["req"].append(name)
- else:
- docs["opt"].append(name)
-
- return docs
-
-
-def test_parse():
- docs = parse_contributing_page()
- assert len(docs["req"]) == len(set(docs["req"]))
- assert len(docs["opt"]) == len(set(docs["opt"]))
- assert len(docs["all"]) == len(set(docs["all"]))
- assert set(docs["all"]) == set(docs["req"] + docs["opt"])
-
-
-@pytest.mark.parametrize("e", [e for e in os.listdir(element_path) if e.endswith(".def")])
-def test_element_page(e):
- with open(os.path.join(element_path, e)) as f:
- data = yaml.load(f, Loader=yaml.FullLoader)
-
- docs = parse_contributing_page()
-
- for key in data:
- assert key in docs["all"]
-
- for key in docs["req"]:
- assert key in data
-
-
-@pytest.mark.parametrize("e", [e for e in os.listdir(element_path) if e.endswith(".def")])
-def test_symfem_no_degreemap(e):
- def no_degreemap(impl):
- if isinstance(impl, dict):
- return all(no_degreemap(i) for i in impl.values())
- return "DEGREEMAP" not in impl
-
- with open(os.path.join(element_path, e)) as f:
- data = yaml.load(f, Loader=yaml.FullLoader)
-
- if "implementations" in data and "symfem" in data["implementations"]:
- assert no_degreemap(data["implementations"]["symfem"])
diff --git a/test/test_implementations.py b/test/test_implementations.py
deleted file mode 100644
index a1c05e6ddf..0000000000
--- a/test/test_implementations.py
+++ /dev/null
@@ -1,10 +0,0 @@
-import pytest
-
-from defelement.implementations import implementations
-from defelement.languages import languages
-
-
-@pytest.mark.parametrize("i", implementations)
-def test_languages(i):
- for lang in implementations[i].languages:
- assert lang in languages
diff --git a/test/test_pages.py b/test/test_pages.py
deleted file mode 100644
index 631abda682..0000000000
--- a/test/test_pages.py
+++ /dev/null
@@ -1,16 +0,0 @@
-import os
-
-import pytest
-
-dir_path = os.path.dirname(os.path.realpath(__file__))
-pages_path = os.path.join(dir_path, "../pages")
-
-md_files = [p for p in os.listdir(pages_path) if p.endswith(".md")]
-
-
-@pytest.mark.parametrize("p", md_files)
-def test_brackets(p):
- with open(os.path.join(pages_path, p)) as f:
- page = f.read()
-
- assert page.count("(") == page.count(")")
diff --git a/test/test_polynomial_sets.py b/test/test_polynomial_sets.py
deleted file mode 100644
index c04f67ec53..0000000000
--- a/test/test_polynomial_sets.py
+++ /dev/null
@@ -1,44 +0,0 @@
-import hashlib
-import os
-import re
-from random import random
-
-import pytest
-import yaml
-
-element_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../elements")
-
-inputs = []
-for i in os.listdir(element_path):
- if i.endswith(".def"):
- with open(os.path.join(element_path, i)) as f:
- data = yaml.load(f, Loader=yaml.FullLoader)
- inputs += [(i, c) for c in data["reference-cells"]]
-
-
-@pytest.mark.parametrize("file, cellname", inputs)
-def test_latex(file, cellname):
- with open(os.path.join(element_path, file)) as f:
- data = yaml.load(f, Loader=yaml.FullLoader)
-
- if "polynomial set" not in data:
- return
-
- for j in data["polynomial set"].values():
- for k in j.split("&&"):
- k = k.strip()
- if k.startswith(""):
- k = re.sub(r"\@def\@([^\@]+)\@([^\@]+)\@", "", k)
- k = re.sub(r"\@defmath\@([^\@]+)\@([^\@]+)\@", "", k)
- filename = "_temp_" + hashlib.sha224(f"{random()}".encode()).hexdigest()
- with open(f"{filename}.tex", "w") as f:
- f.write("\\documentclass{article}\n\n")
- f.write("\\usepackage{amsmath}\n")
- f.write("\\usepackage{amssymb}\n")
- f.write("\\usepackage{amsfonts}\n")
- f.write("\n\\begin{document}\n")
- f.write("\\[\n" + k[4:-1] + "\n\\]")
- f.write("\\end{document}")
- if os.system(f"pdflatex -halt-on-error {filename}.tex > /dev/null") != 0:
- assert os.system(f"pdflatex -halt-on-error {filename}.tex") == 0
- os.system(f"rm {filename}.*")
diff --git a/test/test_sequences.py b/test/test_sequences.py
deleted file mode 100644
index 1ff860ba15..0000000000
--- a/test/test_sequences.py
+++ /dev/null
@@ -1,243 +0,0 @@
-import os
-import re
-import signal
-import urllib
-import urllib.request
-import warnings
-
-import pytest
-import symfem
-import sympy
-import yaml
-
-oeis_cache: dict[str, str] = {}
-
-
-class TimeOutTheTest(BaseException):
- pass
-
-
-def handler(signum, frame):
- raise TimeOutTheTest()
-
-
-def latex_to_pyth(formula):
- formula = re.sub(r"([0-9])\(", r"\1*(", str(formula))
- formula = re.sub(r"([0-9])k", r"\1*k", str(formula))
- formula = formula.replace(")(", ")*(")
- formula = formula.replace("k(", "k*(")
- formula = formula.replace("^", "**")
- formula = formula.replace("/", "//")
- return formula
-
-
-def check_formula(formula, seq):
- if isinstance(formula, list):
- parts = []
- for i, fpart in enumerate(formula):
- k, j = next(iter(fpart.items()))
- if "=" in k:
- ns = [int(a) for a in k.split("=")[1].split(",")]
- elif ">=" in k:
- ns = [a for a in seq if a >= int(k.split(">=")[1])]
- elif ">" in k:
- ns = [a for a in seq if a > int(k.split(">")[1])]
- parts.append((j, ns))
- for k, s in seq.items():
- for i, j in parts:
- if k in j:
- assert s == eval(latex_to_pyth(i).replace("k", str(k)))
- break
- else:
- warnings.warn(f"k={k} is not included in this sequence")
- else:
- for k, s in seq.items():
- assert s == eval(latex_to_pyth(formula).replace("k", str(k)))
-
-
-def is_satisfied(condition, n):
- if condition.startswith("k>="):
- return n >= int(condition[3:])
- if condition.startswith("k<="):
- return n <= int(condition[3:])
- if condition.startswith("k>"):
- return n > int(condition[2:])
- if condition.startswith("k<"):
- return n < int(condition[2:])
- raise ValueError
-
-
-def check_oeis(oeis, seq):
- if " [" in oeis:
- oeis, condition = oeis.split(" [")
- condition = condition.split("]")[0]
- seq = {i: j for i, j in seq.items() if is_satisfied(condition, i)}
- seq = {i: j for i, j in seq.items() if j > 0}
- if oeis not in oeis_cache:
- try:
- with urllib.request.urlopen(
- urllib.request.Request(
- f"http://oeis.org/{oeis}/list",
- headers={"User-Agent": "DefElement test runner"},
- )
- ) as f:
- oeis_cache[oeis] = "".join(
- [
- i.strip()
- for i in f.read()
- .decode("utf-8")
- .split("