From 4c2403b428b323779a5c5a9bb29618e3d3a2c52f Mon Sep 17 00:00:00 2001 From: Matthew Scroggs Date: Mon, 10 Jun 2024 11:44:49 +0100 Subject: [PATCH 01/15] _ --- symfem/elements/hhj.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/symfem/elements/hhj.py b/symfem/elements/hhj.py index fdaa4d7a..3a2d2d3c 100644 --- a/symfem/elements/hhj.py +++ b/symfem/elements/hhj.py @@ -15,7 +15,8 @@ from ..finite_element import CiarletElement from ..functionals import IntegralMoment, ListOfFunctionals, NormalInnerProductIntegralMoment -from ..functions import FunctionInput, MatrixFunction +from ..functions import FunctionInput +from ..functions import MatrixFunction as _MatrixFunction from ..moments import make_integral_moment_dofs from ..polynomials import polynomial_set_vector from ..references import NonDefaultReferenceError, Reference @@ -71,7 +72,7 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" for p, dof in zip(basis, space.dofs): for d in directions: dofs.append(IntegralMoment( - reference, p * MatrixFunction(d), dof, entity=(reference.tdim, 0), + reference, p * _MatrixFunction(d), dof, entity=(reference.tdim, 0), mapping="double_contravariant")) # cell functions extra space_extra = Lagrange(reference, order, variant) @@ -79,7 +80,7 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" for p, dof in zip(basis_extra, space_extra.dofs): for d in directions_extra: dofs.append(IntegralMoment( - reference, p * MatrixFunction(d), dof, entity=(reference.tdim, 0), + reference, p * _MatrixFunction(d), dof, entity=(reference.tdim, 0), mapping="double_contravariant")) self.variant = variant From 329b98e5ea0a5b627f4f066e8974aca56ef536e8 Mon Sep 17 00:00:00 2001 From: Matthew Scroggs Date: Mon, 10 Jun 2024 11:45:31 +0100 Subject: [PATCH 02/15] skip tests --- .github/workflows/run-tests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 903252b5..e6d6a829 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -3,7 +3,8 @@ name: 🧪 Tests on: push: branches: - - "**" +# - "**" + - main pull_request: branches: - main From ca06f092eb53bbeefc9a7432983a5eaa962420c5 Mon Sep 17 00:00:00 2001 From: Matthew Scroggs Date: Mon, 10 Jun 2024 11:48:19 +0100 Subject: [PATCH 03/15] _ --- symfem/mappings.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/symfem/mappings.py b/symfem/mappings.py index bd47dfd9..01b9ab28 100644 --- a/symfem/mappings.py +++ b/symfem/mappings.py @@ -6,7 +6,9 @@ import sympy -from .functions import AnyFunction, FunctionInput, MatrixFunction, parse_function_input +from .functions import AnyFunction, FunctionInput +from .functions import MatrixFunction as _MatrixFunction +from .functions import parse_function_input from .geometry import PointType from .symbols import x @@ -52,7 +54,7 @@ def l2( The mapped function """ tdim = len(inverse_map) - jdet = MatrixFunction([[i.diff(x[j]) for j in range(tdim)] for i in map]).det().as_sympy() + jdet = _MatrixFunction([[i.diff(x[j]) for j in range(tdim)] for i in map]).det().as_sympy() assert isinstance(jdet, sympy.core.expr.Expr) f = parse_function_input(f_in) if substitute: @@ -82,7 +84,7 @@ def covariant( assert f.is_vector - j_inv = MatrixFunction([[i.diff(x[j]) for i in inverse_map] for j in range(tdim)]) + j_inv = _MatrixFunction([[i.diff(x[j]) for i in inverse_map] for j in range(tdim)]) return j_inv @ f @@ -108,7 +110,7 @@ def contravariant( assert f.is_vector - jacobian = MatrixFunction([[i.diff(x[j]) for j in range(tdim)] for i in map]) + jacobian = _MatrixFunction([[i.diff(x[j]) for j in range(tdim)] for i in map]) jacobian /= jacobian.det() return jacobian @ f @@ -116,7 +118,7 @@ def contravariant( def double_covariant( f_in: FunctionInput, map: PointType, inverse_map: PointType, substitute: bool = True, -) -> MatrixFunction: +) -> _MatrixFunction: """Map matrix functions. Args: @@ -134,14 +136,14 @@ def double_covariant( f = f.subs(x, inverse_map) assert f.is_matrix - j_inv = MatrixFunction([[i.diff(x[j]) for i in inverse_map] for j in range(tdim)]) + j_inv = _MatrixFunction([[i.diff(x[j]) for i in inverse_map] for j in range(tdim)]) return j_inv @ f @ j_inv.transpose() def double_contravariant( f_in: FunctionInput, map: PointType, inverse_map: PointType, substitute: bool = True, -) -> MatrixFunction: +) -> _MatrixFunction: """Map matrix functions. Args: @@ -159,7 +161,7 @@ def double_contravariant( f = f.subs(x, inverse_map) assert f.is_matrix - jacobian = MatrixFunction([[i.diff(x[j]) for j in range(tdim)] for i in map]) + jacobian = _MatrixFunction([[i.diff(x[j]) for j in range(tdim)] for i in map]) jacobian /= jacobian.det() return jacobian @ f @ jacobian.transpose() @@ -204,7 +206,7 @@ def l2_inverse_transpose( f = parse_function_input(f_in) if substitute: f = f.subs(x, inverse_map) - jdet = MatrixFunction([[i.diff(x[j]) for j in range(tdim)] for i in map]).det().as_sympy() + jdet = _MatrixFunction([[i.diff(x[j]) for j in range(tdim)] for i in map]).det().as_sympy() assert isinstance(jdet, sympy.core.expr.Expr) return f * abs(jdet) @@ -231,7 +233,7 @@ def covariant_inverse_transpose( assert f.is_vector - jacobian = MatrixFunction([[i.diff(x[j]) for j in range(tdim)] for i in map]) + jacobian = _MatrixFunction([[i.diff(x[j]) for j in range(tdim)] for i in map]) return jacobian @ f @@ -257,7 +259,7 @@ def contravariant_inverse_transpose( assert f.is_vector - j_inv = MatrixFunction([[i.diff(x[j]) for i in inverse_map] for j in range(tdim)]) + j_inv = _MatrixFunction([[i.diff(x[j]) for i in inverse_map] for j in range(tdim)]) j_inv /= j_inv.det() return j_inv @ f From 5bcab5f80b9ed57aec7594c67f651213654e93c3 Mon Sep 17 00:00:00 2001 From: Matthew Scroggs Date: Mon, 10 Jun 2024 11:51:37 +0100 Subject: [PATCH 04/15] symfem. --- symfem/mappings.py | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/symfem/mappings.py b/symfem/mappings.py index 01b9ab28..4117e0ce 100644 --- a/symfem/mappings.py +++ b/symfem/mappings.py @@ -6,11 +6,9 @@ import sympy -from .functions import AnyFunction, FunctionInput -from .functions import MatrixFunction as _MatrixFunction -from .functions import parse_function_input -from .geometry import PointType -from .symbols import x +from symfem.functions import AnyFunction, FunctionInput, MatrixFunction, parse_function_input +from symfem.geometry import PointType +from symfem.symbols import x class MappingNotImplemented(NotImplementedError): @@ -54,7 +52,7 @@ def l2( The mapped function """ tdim = len(inverse_map) - jdet = _MatrixFunction([[i.diff(x[j]) for j in range(tdim)] for i in map]).det().as_sympy() + jdet = MatrixFunction([[i.diff(x[j]) for j in range(tdim)] for i in map]).det().as_sympy() assert isinstance(jdet, sympy.core.expr.Expr) f = parse_function_input(f_in) if substitute: @@ -84,7 +82,7 @@ def covariant( assert f.is_vector - j_inv = _MatrixFunction([[i.diff(x[j]) for i in inverse_map] for j in range(tdim)]) + j_inv = MatrixFunction([[i.diff(x[j]) for i in inverse_map] for j in range(tdim)]) return j_inv @ f @@ -110,7 +108,7 @@ def contravariant( assert f.is_vector - jacobian = _MatrixFunction([[i.diff(x[j]) for j in range(tdim)] for i in map]) + jacobian = MatrixFunction([[i.diff(x[j]) for j in range(tdim)] for i in map]) jacobian /= jacobian.det() return jacobian @ f @@ -118,7 +116,7 @@ def contravariant( def double_covariant( f_in: FunctionInput, map: PointType, inverse_map: PointType, substitute: bool = True, -) -> _MatrixFunction: +) -> MatrixFunction: """Map matrix functions. Args: @@ -136,14 +134,14 @@ def double_covariant( f = f.subs(x, inverse_map) assert f.is_matrix - j_inv = _MatrixFunction([[i.diff(x[j]) for i in inverse_map] for j in range(tdim)]) + j_inv = MatrixFunction([[i.diff(x[j]) for i in inverse_map] for j in range(tdim)]) return j_inv @ f @ j_inv.transpose() def double_contravariant( f_in: FunctionInput, map: PointType, inverse_map: PointType, substitute: bool = True, -) -> _MatrixFunction: +) -> MatrixFunction: """Map matrix functions. Args: @@ -161,7 +159,7 @@ def double_contravariant( f = f.subs(x, inverse_map) assert f.is_matrix - jacobian = _MatrixFunction([[i.diff(x[j]) for j in range(tdim)] for i in map]) + jacobian = MatrixFunction([[i.diff(x[j]) for j in range(tdim)] for i in map]) jacobian /= jacobian.det() return jacobian @ f @ jacobian.transpose() @@ -206,7 +204,7 @@ def l2_inverse_transpose( f = parse_function_input(f_in) if substitute: f = f.subs(x, inverse_map) - jdet = _MatrixFunction([[i.diff(x[j]) for j in range(tdim)] for i in map]).det().as_sympy() + jdet = MatrixFunction([[i.diff(x[j]) for j in range(tdim)] for i in map]).det().as_sympy() assert isinstance(jdet, sympy.core.expr.Expr) return f * abs(jdet) @@ -233,7 +231,7 @@ def covariant_inverse_transpose( assert f.is_vector - jacobian = _MatrixFunction([[i.diff(x[j]) for j in range(tdim)] for i in map]) + jacobian = MatrixFunction([[i.diff(x[j]) for j in range(tdim)] for i in map]) return jacobian @ f @@ -259,7 +257,7 @@ def contravariant_inverse_transpose( assert f.is_vector - j_inv = _MatrixFunction([[i.diff(x[j]) for i in inverse_map] for j in range(tdim)]) + j_inv = MatrixFunction([[i.diff(x[j]) for i in inverse_map] for j in range(tdim)]) j_inv /= j_inv.det() return j_inv @ f From 4b2d6c726972061378effd73082adde02983fc76 Mon Sep 17 00:00:00 2001 From: Matthew Scroggs Date: Mon, 10 Jun 2024 11:57:48 +0100 Subject: [PATCH 05/15] ruff format --- .github/scripts/trigger_rtd_build.py | 4 +- .github/scripts/version.py | 29 +- .github/workflows/style-checks.yml | 4 +- demo/lagrange.py | 6 +- demo/test_demos.py | 18 +- docs/conf.py | 4 +- prepare_release.py | 3 +- pyproject.toml | 1 + scripts/draw_logo.py | 251 +++++-- scripts/draw_references.py | 17 +- scripts/generate_gn.py | 23 +- scripts/test_scripts.py | 22 +- symfem/__init__.py | 4 +- symfem/basis_functions.py | 31 +- symfem/create.py | 24 +- symfem/elements/_guzman_neilan_tetrahedron.py | 235 +++++-- symfem/elements/_guzman_neilan_triangle.py | 30 +- symfem/elements/abf.py | 39 +- symfem/elements/ac.py | 16 +- symfem/elements/alfeld_sorokina.py | 103 ++- symfem/elements/argyris.py | 29 +- symfem/elements/aw.py | 132 +++- symfem/elements/bddm.py | 2 +- symfem/elements/bell.py | 6 +- symfem/elements/bernardi_raugel.py | 57 +- symfem/elements/bernstein.py | 62 +- symfem/elements/bfs.py | 18 +- symfem/elements/bubble.py | 40 +- .../elements/conforming_crouzeix_raviart.py | 13 +- symfem/elements/crouzeix_raviart.py | 22 +- symfem/elements/direct_serendipity.py | 11 +- symfem/elements/dpc.py | 21 +- symfem/elements/dual.py | 112 ++-- symfem/elements/fortin_soulie.py | 2 +- symfem/elements/guzman_neilan.py | 132 ++-- symfem/elements/hct.py | 101 ++- symfem/elements/hermite.py | 11 +- symfem/elements/hhj.py | 70 +- symfem/elements/huang_zhang.py | 20 +- symfem/elements/kmv.py | 157 +++-- symfem/elements/lagrange.py | 98 +-- symfem/elements/lagrange_prism.py | 37 +- symfem/elements/lagrange_pyramid.py | 43 +- symfem/elements/morley.py | 3 +- symfem/elements/morley_wang_xu.py | 46 +- symfem/elements/mtw.py | 62 +- symfem/elements/nedelec.py | 9 +- symfem/elements/nedelec_prism.py | 45 +- symfem/elements/p1_iso_p2.py | 47 +- symfem/elements/p1_macro.py | 18 +- symfem/elements/q.py | 59 +- symfem/elements/rannacher_turek.py | 4 +- symfem/elements/regge.py | 283 +++++--- symfem/elements/rhct.py | 70 +- symfem/elements/rt.py | 12 +- symfem/elements/serendipity.py | 27 +- symfem/elements/taylor.py | 7 +- symfem/elements/tnt.py | 264 +++++--- symfem/elements/transition.py | 24 +- symfem/elements/trimmed_serendipity.py | 105 +-- symfem/elements/vector_enriched_galerkin.py | 7 +- symfem/elements/wu_xu.py | 31 +- symfem/finite_element.py | 301 ++++++--- symfem/functionals.py | 348 +++++++--- symfem/functions.py | 335 ++++++---- symfem/geometry.py | 9 +- symfem/mappings.py | 61 +- symfem/moments.py | 28 +- symfem/piecewise_functions.py | 155 +++-- symfem/plotting.py | 380 +++++++---- symfem/polynomials/__init__.py | 25 +- symfem/polynomials/dual.py | 1 + symfem/polynomials/legendre.py | 239 ++++--- symfem/polynomials/lobatto.py | 20 +- symfem/polynomials/polysets.py | 144 ++-- symfem/quadrature.py | 152 +++-- symfem/references.py | 627 +++++++++++------- symfem/symbols.py | 4 +- symfem/utils.py | 2 +- test/test_against_basix.py | 158 +++-- test/test_against_computed_by_hand.py | 37 +- test/test_alfeld_sorokina.py | 6 +- test/test_bell.py | 3 +- test/test_bernstein.py | 7 +- test/test_docs.py | 20 +- test/test_dof_descriptions.py | 16 +- test/test_elements.py | 30 +- test/test_functions.py | 74 +-- test/test_guzman_neilan.py | 26 +- test/test_hct.py | 11 +- test/test_hellan_herrmann_johnson.py | 5 +- test/test_lagrange_polynomial_variants.py | 32 +- test/test_mapping.py | 67 +- test/test_maximum_degree.py | 6 +- test/test_nedelec.py | 29 +- test/test_plotting.py | 246 ++++--- test/test_polynomials.py | 93 +-- test/test_quadrature.py | 16 +- test/test_references.py | 36 +- test/test_stiffness_matrix.py | 2 +- test/test_tensor_product.py | 60 +- test/utils.py | 143 ++-- update_readme.py | 19 +- 103 files changed, 4672 insertions(+), 2484 deletions(-) diff --git a/.github/scripts/trigger_rtd_build.py b/.github/scripts/trigger_rtd_build.py index cdaefc39..a45d6fec 100644 --- a/.github/scripts/trigger_rtd_build.py +++ b/.github/scripts/trigger_rtd_build.py @@ -2,8 +2,8 @@ import requests -URL = 'https://readthedocs.org/api/v3/projects/symfem/versions/latest/builds/' +URL = "https://readthedocs.org/api/v3/projects/symfem/versions/latest/builds/" TOKEN = sys.argv[-1] -HEADERS = {'Authorization': f'token {TOKEN}'} +HEADERS = {"Authorization": f"token {TOKEN}"} response = requests.post(URL, headers=HEADERS) print(response.json()) diff --git a/.github/scripts/version.py b/.github/scripts/version.py index aecdffd6..80b8cd4c 100644 --- a/.github/scripts/version.py +++ b/.github/scripts/version.py @@ -35,22 +35,35 @@ raise RuntimeError("CHANGELOG_SINCE_LAST_VERSION.md should not be empty") symfem.create_git_tag_and_release( - f"v{version}", f"Version {version}", f"Version {version}", changes, - branch.commit.sha, "commit") + f"v{version}", + f"Version {version}", + f"Version {version}", + changes, + branch.commit.sha, + "commit", + ) old_changelog_file = symfem.get_contents("CHANGELOG.md", new_branch.commit.sha) old_changes = old_changelog_file.decoded_content.decode("utf8").strip() - new_changelog = (f"# Version {version} ({datetime.now().strftime('%d %B %Y')})\n\n" - f"{changes}\n\n{old_changes}\n") + new_changelog = ( + f"# Version {version} ({datetime.now().strftime('%d %B %Y')})\n\n" + f"{changes}\n\n{old_changes}\n" + ) symfem.update_file( - "CHANGELOG.md", "Update CHANGELOG.md", new_changelog, sha=old_changelog_file.sha, - branch=f"v{version}-changelog" + "CHANGELOG.md", + "Update CHANGELOG.md", + new_changelog, + sha=old_changelog_file.sha, + branch=f"v{version}-changelog", ) symfem.update_file( - "CHANGELOG_SINCE_LAST_VERSION.md", "Reset CHANGELOG_SINCE_LAST_VERSION.md", "", - sha=changelog_file.sha, branch=f"v{version}-changelog" + "CHANGELOG_SINCE_LAST_VERSION.md", + "Reset CHANGELOG_SINCE_LAST_VERSION.md", + "", + sha=changelog_file.sha, + branch=f"v{version}-changelog", ) symfem.create_pull( diff --git a/.github/workflows/style-checks.yml b/.github/workflows/style-checks.yml index 78912f88..9b6a5839 100644 --- a/.github/workflows/style-checks.yml +++ b/.github/workflows/style-checks.yml @@ -16,7 +16,9 @@ jobs: - uses: actions/checkout@v4 - run: sudo apt-get install -y python3-setuptools - run: pip3 install -e .[style,docs] - - run: python3 -m ruff check . + - run: | + python3 -m ruff check . + python3 -m ruff format --check . name: Run ruff checks - run: python3 -m flake8 . name: Run flake8 checks diff --git a/demo/lagrange.py b/demo/lagrange.py index 91327520..ebb2ad93 100644 --- a/demo/lagrange.py +++ b/demo/lagrange.py @@ -29,14 +29,14 @@ # Check that the basis functions on this edge are equal for d, edge_f in zip(dofs, edge_basis): # allequal will simplify the expressions then check that they are equal - assert allequal(basis[d].subs(x[:2], (1 - a, a)), edge_f) + assert allequal(basis[d].subs(x[:2], (1 - a, a)), edge_f) # Get the DOFs on edge 1 (from vertex 0 (0,0) to vertex 2 (0,1), parametrised (0, a)) dofs = element.entity_dofs(0, 0) + element.entity_dofs(0, 2) + element.entity_dofs(1, 1) for d, edge_f in zip(dofs, edge_basis): - assert allequal(basis[d].subs(x[:2], (0, a)), edge_f) + assert allequal(basis[d].subs(x[:2], (0, a)), edge_f) # Get the DOFs on edge 2 (from vertex 0 (0,0) to vertex 1 (1,0), parametrised (a, 0)) dofs = element.entity_dofs(0, 0) + element.entity_dofs(0, 1) + element.entity_dofs(1, 2) for d, edge_f in zip(dofs, edge_basis): - assert allequal(basis[d].subs(x[:2], (a, 0)), edge_f) + assert allequal(basis[d].subs(x[:2], (a, 0)), edge_f) diff --git a/demo/test_demos.py b/demo/test_demos.py index 5c42ea5d..1c95042e 100644 --- a/demo/test_demos.py +++ b/demo/test_demos.py @@ -5,15 +5,15 @@ import pytest -@pytest.mark.parametrize("file", [ - file - for file in os.listdir(os.path.dirname(os.path.realpath(__file__))) - if file.endswith(".py") and not file.startswith(".") and not file.startswith("test_") -]) -def test_demo(file): - file_path = os.path.join( - os.path.dirname(os.path.realpath(__file__)), +@pytest.mark.parametrize( + "file", + [ file - ) + for file in os.listdir(os.path.dirname(os.path.realpath(__file__))) + if file.endswith(".py") and not file.startswith(".") and not file.startswith("test_") + ], +) +def test_demo(file): + file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), file) assert os.system(f"python3 {file_path}") == 0 diff --git a/docs/conf.py b/docs/conf.py index ae76ddb3..85886d5d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -44,7 +44,7 @@ "sphinx.ext.coverage", "sphinx.ext.mathjax", "autoapi.extension", - "sphinx.ext.napoleon" + "sphinx.ext.napoleon", ] # Add any paths that contain templates here, relative to this directory. @@ -118,7 +118,7 @@ latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # - 'papersize': 'a4paper', + "papersize": "a4paper", # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', diff --git a/prepare_release.py b/prepare_release.py index 79025f31..55281775 100644 --- a/prepare_release.py +++ b/prepare_release.py @@ -3,8 +3,7 @@ import argparse parser = argparse.ArgumentParser(description="Build defelement.com") -parser.add_argument('--version', metavar='version', - default="main", help="Symfem version.") +parser.add_argument("--version", metavar="version", default="main", help="Symfem version.") version = parser.parse_args().version if version != "main": version = "v" + version diff --git a/pyproject.toml b/pyproject.toml index c404568c..cf40b5e0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,7 @@ test = ["pytest", "symfem[optional]", "numpy"] [tool.ruff] line-length = 100 +indent-width = 4 [tool.ruff.lint.per-file-ignores] "__init__.py" = ["F401"] diff --git a/scripts/draw_logo.py b/scripts/draw_logo.py index 466d4b3d..2894a111 100644 --- a/scripts/draw_logo.py +++ b/scripts/draw_logo.py @@ -17,53 +17,142 @@ letters = [ # S - [[(0, 0), (2, 0), (3, 1), (3, 3), (1, 3), (0, 2), (0, 0)], - [(0, 1), (2, 1)], - [(1, 2), (3, 2)]], + [[(0, 0), (2, 0), (3, 1), (3, 3), (1, 3), (0, 2), (0, 0)], [(0, 1), (2, 1)], [(1, 2), (3, 2)]], # Y - [[(0, 0), (2, 0), (3, 1), (3, 3), (2, 3), (2, 2), (1, 2), (1, 3), (0, 2), (0, 0)], - [(0, 1), (2, 1)]], + [ + [(0, 0), (2, 0), (3, 1), (3, 3), (2, 3), (2, 2), (1, 2), (1, 3), (0, 2), (0, 0)], + [(0, 1), (2, 1)], + ], # M - [[(0, 0), (1, 0), (1, 1), (2, 1), (2, 0), (3, 1), (3, 3), (1, 3), (0, 2), (0, 0)], - [(1, 1), (1, 2)], - [(2, 1), (2, 2)]], + [ + [(0, 0), (1, 0), (1, 1), (2, 1), (2, 0), (3, 1), (3, 3), (1, 3), (0, 2), (0, 0)], + [(1, 1), (1, 2)], + [(2, 1), (2, 2)], + ], # F - [[(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (3, 2), (3, 3), (1, 3), (0, 2), (0, 0)], - [(1, 2), (2, 2)]], + [ + [(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (3, 2), (3, 3), (1, 3), (0, 2), (0, 0)], + [(1, 2), (2, 2)], + ], # E - [[(0, 0), (2, 0), (3, 1), (2, 1), (2, 2), (3, 2), (3, 3), (1, 3), (0, 2), (0, 0)], - [(1, 1), (2, 1)], - [(1, 2), (2, 2)]], + [ + [(0, 0), (2, 0), (3, 1), (2, 1), (2, 2), (3, 2), (3, 3), (1, 3), (0, 2), (0, 0)], + [(1, 1), (2, 1)], + [(1, 2), (2, 2)], + ], # M - [[(0, 0), (1, 0), (1, 1), (2, 1), (2, 0), (3, 1), (3, 3), (1, 3), (0, 2), (0, 0)], - [(1, 1), (1, 2)], - [(2, 1), (2, 2)]] + [ + [(0, 0), (1, 0), (1, 1), (2, 1), (2, 0), (3, 1), (3, 3), (1, 3), (0, 2), (0, 0)], + [(1, 1), (1, 2)], + [(2, 1), (2, 2)], + ], ] ups = [ # S - [(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (3, 1), - (0, 2), (1, 2), (2, 2), (3, 2), (1, 3), (2, 3), (3, 3)], + [ + (0, 0), + (1, 0), + (2, 0), + (0, 1), + (1, 1), + (2, 1), + (3, 1), + (0, 2), + (1, 2), + (2, 2), + (3, 2), + (1, 3), + (2, 3), + (3, 3), + ], # Y - [(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (3, 1), - (0, 2), (1, 2), (2, 2), (3, 2), (1, 3), (2, 3), (3, 3)], + [ + (0, 0), + (1, 0), + (2, 0), + (0, 1), + (1, 1), + (2, 1), + (3, 1), + (0, 2), + (1, 2), + (2, 2), + (3, 2), + (1, 3), + (2, 3), + (3, 3), + ], # M - [(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (3, 1), - (0, 2), (1, 2), (2, 2), (3, 2), (1, 3), (2, 3), (3, 3)], + [ + (0, 0), + (1, 0), + (2, 0), + (0, 1), + (1, 1), + (2, 1), + (3, 1), + (0, 2), + (1, 2), + (2, 2), + (3, 2), + (1, 3), + (2, 3), + (3, 3), + ], # F - [(0, 0), (1, 0), (0, 1), (1, 1), (2, 1), - (0, 2), (1, 2), (2, 2), (3, 2), (1, 3), (2, 3), (3, 3)], + [ + (0, 0), + (1, 0), + (0, 1), + (1, 1), + (2, 1), + (0, 2), + (1, 2), + (2, 2), + (3, 2), + (1, 3), + (2, 3), + (3, 3), + ], # E - [(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (3, 1), - (0, 2), (1, 2), (2, 2), (3, 2), (1, 3), (2, 3), (3, 3)], + [ + (0, 0), + (1, 0), + (2, 0), + (0, 1), + (1, 1), + (2, 1), + (3, 1), + (0, 2), + (1, 2), + (2, 2), + (3, 2), + (1, 3), + (2, 3), + (3, 3), + ], # M - [(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (3, 1), - (0, 2), (1, 2), (2, 2), (3, 2), (1, 3), (2, 3), (3, 3)] + [ + (0, 0), + (1, 0), + (2, 0), + (0, 1), + (1, 1), + (2, 1), + (3, 1), + (0, 2), + (1, 2), + (2, 2), + (3, 2), + (1, 3), + (2, 3), + (3, 3), + ], ] for n, letter in enumerate(letters): - letters[n] = [[(4 * n + j[0], j[1]) for j in i] - for i in letter] + letters[n] = [[(4 * n + j[0], j[1]) for j in i] for i in letter] all_ups = [] for n, up in enumerate(ups): ups[n] = [(4 * n + j[0], j[1]) for j in up] @@ -72,7 +161,7 @@ def to_2d(x, y, z): """Project a point to 2d.""" - return (90 + (x + (-z+y/2-3)/2) * 30, 68 + (3-y - z) * sqrt(3)/2 * 30) + return (90 + (x + (-z + y / 2 - 3) / 2) * 30, 68 + (3 - y - z) * sqrt(3) / 2 * 30) def zvalue(x, y, z): @@ -90,15 +179,17 @@ def in_letter(a, b, c): return False -svg = ("\n") +svg = ( + "\n" +) polys = [] for x in range(-4, 26): for y in range(-3, 7): p1 = (x, y) - p3 = (x+1, y+1) - for p2 in [(x+1, y), (x, y+1)]: + p3 = (x + 1, y + 1) + for p2 in [(x + 1, y), (x, y + 1)]: z1 = 1.3 if p1 in all_ups else 0 z2 = 1.3 if p2 in all_ups else 0 z3 = 1.3 if p3 in all_ups else 0 @@ -111,7 +202,8 @@ def in_letter(a, b, c): normal: typing.Tuple[typing.Union[int, float], ...] = ( edge1[1] * edge2[2] - edge1[2] * edge2[1], edge1[2] * edge2[0] - edge1[0] * edge2[2], - edge1[0] * edge2[1] - edge1[1] * edge2[0]) + edge1[0] * edge2[1] - edge1[1] * edge2[0], + ) if p2[0] == x: normal = tuple(-i for i in normal) size = sqrt(sum(i**2 for i in normal)) @@ -127,16 +219,16 @@ def in_letter(a, b, c): color = "#BBBBBB" strokecolor = "black" - mid = ((p1[0] + p2[0] + p3[0]) / 3, - (p1[1] + p2[1] + p3[1]) / 3, - (z1 + z2 + z3) / 3) - polys.append(( - zvalue(*mid), - [to_2d(*p1, z1), to_2d(*p2, z2), to_2d(*p3, z3)], - color, - strokecolor, - in_letter(p1, p2, p3) and p1[0] < 4 - )) + mid = ((p1[0] + p2[0] + p3[0]) / 3, (p1[1] + p2[1] + p3[1]) / 3, (z1 + z2 + z3) / 3) + polys.append( + ( + zvalue(*mid), + [to_2d(*p1, z1), to_2d(*p2, z2), to_2d(*p3, z3)], + color, + strokecolor, + in_letter(p1, p2, p3) and p1[0] < 4, + ) + ) polys.sort(key=lambda p: p[0]) @@ -150,8 +242,10 @@ def in_letter(a, b, c): for i, j in zip(line_group[:-1], line_group[1:]): x1, y1 = to_2d(*i, 1.3) x2, y2 = to_2d(*j, 1.3) - svg += (f"\n") + svg += ( + f"\n" + ) svg += "" @@ -162,18 +256,23 @@ def in_letter(a, b, c): def to_2d_new(x, y, z): """Project a point to 2d.""" - return (1.6 * (90 + (x + (-z+y/2-3)/2) * 30), 160 + 1.6 * (68 + (3-y - z) * sqrt(3)/2 * 30)) + return ( + 1.6 * (90 + (x + (-z + y / 2 - 3) / 2) * 30), + 160 + 1.6 * (68 + (3 - y - z) * sqrt(3) / 2 * 30), + ) -svg = ("\n") +svg = ( + "\n" +) polys = [] for x in range(-4, 27): for y in range(-7, 10): p1 = (x, y) - p3 = (x+1, y+1) - for p2 in [(x+1, y), (x, y+1)]: + p3 = (x + 1, y + 1) + for p2 in [(x + 1, y), (x, y + 1)]: z1 = 1.3 if p1 in all_ups else 0 z2 = 1.3 if p2 in all_ups else 0 z3 = 1.3 if p3 in all_ups else 0 @@ -183,9 +282,11 @@ def to_2d_new(x, y, z): else: edge1 = (p2[0] - p1[0], p2[1] - p1[1], z2 - z1) edge2 = (p3[0] - p1[0], p3[1] - p1[1], z3 - z1) - normal = (edge1[1] * edge2[2] - edge1[2] * edge2[1], - edge1[2] * edge2[0] - edge1[0] * edge2[2], - edge1[0] * edge2[1] - edge1[1] * edge2[0]) + normal = ( + edge1[1] * edge2[2] - edge1[2] * edge2[1], + edge1[2] * edge2[0] - edge1[0] * edge2[2], + edge1[0] * edge2[1] - edge1[1] * edge2[0], + ) if p2[0] == x: normal = tuple(-i for i in normal) size = sqrt(sum(i**2 for i in normal)) @@ -201,16 +302,16 @@ def to_2d_new(x, y, z): color = "#BBBBBB" strokecolor = "black" - mid = ((p1[0] + p2[0] + p3[0]) / 3, - (p1[1] + p2[1] + p3[1]) / 3, - (z1 + z2 + z3) / 3) - polys.append(( - zvalue(*mid), - [to_2d_new(*p1, z1), to_2d_new(*p2, z2), to_2d_new(*p3, z3)], - color, - strokecolor, - in_letter(p1, p2, p3) and p1[0] < 4 - )) + mid = ((p1[0] + p2[0] + p3[0]) / 3, (p1[1] + p2[1] + p3[1]) / 3, (z1 + z2 + z3) / 3) + polys.append( + ( + zvalue(*mid), + [to_2d_new(*p1, z1), to_2d_new(*p2, z2), to_2d_new(*p3, z3)], + color, + strokecolor, + in_letter(p1, p2, p3) and p1[0] < 4, + ) + ) polys.sort(key=lambda p: p[0]) @@ -224,8 +325,10 @@ def to_2d_new(x, y, z): for i, j in zip(line_group[:-1], line_group[1:]): x1, y1 = to_2d_new(*i, 1.3) x2, y2 = to_2d_new(*j, 1.3) - svg += (f"\n") + svg += ( + f"\n" + ) svg += "" @@ -239,8 +342,10 @@ def fav_move(p): return p[0] - 10, p[1] - 150 -svg = ("\n") +svg = ( + "\n" +) for p in polys: if p[4]: @@ -253,8 +358,10 @@ def fav_move(p): for i, j in zip(line_group[:-1], line_group[1:]): x1, y1 = fav_move(to_2d_new(*i, 1.3)) x2, y2 = fav_move(to_2d_new(*j, 1.3)) - svg += (f"\n") + svg += ( + f"\n" + ) svg += "" diff --git a/scripts/draw_references.py b/scripts/draw_references.py index 4e375fe5..7cf45766 100644 --- a/scripts/draw_references.py +++ b/scripts/draw_references.py @@ -12,9 +12,16 @@ else: folder = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../img") -for shape in ["interval", "triangle", "tetrahedron", - "quadrilateral", "hexahedron", "prism", "pyramid", - "dual polygon"]: +for shape in [ + "interval", + "triangle", + "tetrahedron", + "quadrilateral", + "hexahedron", + "prism", + "pyramid", + "dual polygon", +]: if shape == "dual polygon": ref = symfem.create_reference("dual polygon(6)") else: @@ -22,5 +29,5 @@ filename = shape.replace(" ", "_") + "_numbering" ref.plot_entity_diagrams( - f"{folder}/{filename}.png", {"png_width": 207 * (ref.tdim + 1)}, - scale=300) + f"{folder}/{filename}.png", {"png_width": 207 * (ref.tdim + 1)}, scale=300 + ) diff --git a/scripts/generate_gn.py b/scripts/generate_gn.py index 6c01ba5b..39163399 100644 --- a/scripts/generate_gn.py +++ b/scripts/generate_gn.py @@ -43,7 +43,6 @@ def find_solution(mat, aim): for ref in ["triangle", "tetrahedron"]: - # TODO: work out why tetrahedron fails on github but passes locally if TESTING and ref == "tetrahedron": break @@ -59,7 +58,8 @@ def find_solution(mat, aim): sub_cells = [ (reference.vertices[0], reference.vertices[1], mid), (reference.vertices[0], reference.vertices[2], mid), - (reference.vertices[1], reference.vertices[2], mid)] + (reference.vertices[1], reference.vertices[2], mid), + ] xx, yy, zz = x terms = [1, xx, yy] if ref == "tetrahedron": @@ -68,27 +68,21 @@ def find_solution(mat, aim): (reference.vertices[0], reference.vertices[1], reference.vertices[2], mid), (reference.vertices[0], reference.vertices[1], reference.vertices[3], mid), (reference.vertices[0], reference.vertices[2], reference.vertices[3], mid), - (reference.vertices[1], reference.vertices[2], reference.vertices[3], mid)] + (reference.vertices[1], reference.vertices[2], reference.vertices[3], mid), + ] xx, yy, zz = x - terms = [1, xx, yy, zz, xx**2, yy**2, zz**2, xx*yy, xx*zz, yy*zz] + terms = [1, xx, yy, zz, xx**2, yy**2, zz**2, xx * yy, xx * zz, yy * zz] sub_basis = make_piecewise_lagrange(sub_cells, ref, br.reference.tdim, True) filename = os.path.join(folder, f"_guzman_neilan_{ref}.py") - output = ( - "\"\"\"Values for Guzman-Neilan element.\"\"\"\n" - "\n" - "import sympy\n" - "\n" - "coeffs = [\n") + output = '"""Values for Guzman-Neilan element."""\n' "\n" "import sympy\n" "\n" "coeffs = [\n" for f in fs: assert isinstance(f, VectorFunction) output += " [\n" integrand = f.div().subs(x, t) - fun_s = ( - f.div() - integrand.integral(reference) / reference.volume() - ).as_sympy() + fun_s = (f.div() - integrand.integral(reference) / reference.volume()).as_sympy() assert isinstance(fun_s, sympy.core.expr.Expr) fun = fun_s.as_coefficients_dict() @@ -97,7 +91,8 @@ def find_solution(mat, aim): aim = [fun[term] if term in fun else 0 for term in terms] * (br.reference.tdim + 1) mat: typing.List[typing.List[symfem.functions.ScalarFunction]] = [ - [] for t in terms for p in sub_basis[0].pieces] + [] for t in terms for p in sub_basis[0].pieces + ] for b in sub_basis: i = 0 for p in b.pieces.values(): diff --git a/scripts/test_scripts.py b/scripts/test_scripts.py index 44404ea3..a9f8854c 100644 --- a/scripts/test_scripts.py +++ b/scripts/test_scripts.py @@ -2,21 +2,19 @@ import pytest -file_path = os.path.join( - os.path.dirname(os.path.realpath(__file__)), "../_temp" -) +file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../_temp") os.system(f"mkdir {file_path}") -@pytest.mark.parametrize("file", [ - file - for file in os.listdir(os.path.dirname(os.path.realpath(__file__))) - if file.endswith(".py") and not file.startswith(".") and not file.startswith("test_") -]) -def test_demo(file): - file_path = os.path.join( - os.path.dirname(os.path.realpath(__file__)), +@pytest.mark.parametrize( + "file", + [ file - ) + for file in os.listdir(os.path.dirname(os.path.realpath(__file__))) + if file.endswith(".py") and not file.startswith(".") and not file.startswith("test_") + ], +) +def test_demo(file): + file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), file) assert os.system(f"python3 {file_path} test") == 0 diff --git a/symfem/__init__.py b/symfem/__init__.py index f8e2c010..43c0d1cf 100644 --- a/symfem/__init__.py +++ b/symfem/__init__.py @@ -1,7 +1,7 @@ """Symfem: a symbolic finite element definition library.""" -from .create import add_element, create_element, create_reference -from .version import version as __version__ +from symfem.create import add_element, create_element, create_reference +from symfem.version import version as __version__ __citation__ = "https://doi.org/10.21105/joss.03556 (Scroggs, 2021)" __github__ = "https://github.com/mscroggs/symfem" diff --git a/symfem/basis_functions.py b/symfem/basis_functions.py index 3215a4cb..161ec054 100644 --- a/symfem/basis_functions.py +++ b/symfem/basis_functions.py @@ -9,10 +9,16 @@ import symfem -from .functions import AnyFunction, FunctionInput, ScalarFunction, SympyFormat, ValuesToSubstitute -from .geometry import PointType -from .references import Reference -from .symbols import AxisVariables, AxisVariablesNotSingle, t, x +from symfem.functions import ( + AnyFunction, + FunctionInput, + ScalarFunction, + SympyFormat, + ValuesToSubstitute, +) +from symfem.geometry import PointType +from symfem.references import Reference +from symfem.symbols import AxisVariables, AxisVariablesNotSingle, t, x class BasisFunction(AnyFunction): @@ -285,8 +291,10 @@ def norm(self) -> ScalarFunction: raise self.get_function().norm() def integral( - self, domain: Reference, vars: AxisVariablesNotSingle = x, - dummy_vars: AxisVariablesNotSingle = t + self, + domain: Reference, + vars: AxisVariablesNotSingle = x, + dummy_vars: AxisVariablesNotSingle = t, ) -> ScalarFunction: """Compute the integral of the function. @@ -375,9 +383,7 @@ def maximum_degree(self, cell: symfem.references.Reference) -> int: class SubbedBasisFunction(BasisFunction): """A basis function following a substitution.""" - def __init__( - self, f: BasisFunction, vars: AxisVariables, values: ValuesToSubstitute - ): + def __init__(self, f: BasisFunction, vars: AxisVariables, values: ValuesToSubstitute): """Create a basis function following a substitution. Args: @@ -408,8 +414,11 @@ def shape(self) -> typing.Tuple[int, ...]: return self.f.get_function().shape def plot_values( - self, reference: Reference, img: typing.Any, - value_scale: sympy.core.expr.Expr = sympy.Integer(1), n: int = 6 + self, + reference: Reference, + img: typing.Any, + value_scale: sympy.core.expr.Expr = sympy.Integer(1), + n: int = 6, ): """Plot the function's values. diff --git a/symfem/create.py b/symfem/create.py index 3dd3b02f..293f5e67 100644 --- a/symfem/create.py +++ b/symfem/create.py @@ -4,10 +4,10 @@ import os as _os import typing as _typing -from . import references as _references -from .finite_element import FiniteElement as _FiniteElement -from .geometry import SetOfPointsInput as _SetOfPointsInput -from .geometry import parse_set_of_points_input as _parse_set_of_points_input +import symfem.references as _references +from symfem.finite_element import FiniteElement as _FiniteElement +from symfem.geometry import SetOfPointsInput as _SetOfPointsInput +from symfem.geometry import parse_set_of_points_input as _parse_set_of_points_input _folder = _os.path.dirname(_os.path.realpath(__file__)) @@ -98,8 +98,11 @@ def create_reference( def create_element( - cell_type: str, element_type: str, order: int, - vertices: _typing.Optional[_SetOfPointsInput] = None, **kwargs: _typing.Any + cell_type: str, + element_type: str, + order: int, + vertices: _typing.Optional[_SetOfPointsInput] = None, + **kwargs: _typing.Any, ) -> _FiniteElement: """Make a finite element. @@ -178,8 +181,9 @@ def create_element( reference = create_reference(cell_type, vertices=vertices) if reference.tdim != reference.gdim: - raise ValueError("Cannot create element on cell with different " - "topological and geometric dimensions.") + raise ValueError( + "Cannot create element on cell with different " "topological and geometric dimensions." + ) if element_type in _elementmap: if reference.name not in _elementmap[element_type]: @@ -192,9 +196,7 @@ def create_element( raise ValueError(f"Unsupported element type: {element_type}") -def _order_is_allowed( - element_class: _typing.Type, ref: str, order: int -) -> bool: +def _order_is_allowed(element_class: _typing.Type, ref: str, order: int) -> bool: """Check that an order is valid for an element. Args: diff --git a/symfem/elements/_guzman_neilan_tetrahedron.py b/symfem/elements/_guzman_neilan_tetrahedron.py index d183d21b..aaa5fa78 100644 --- a/symfem/elements/_guzman_neilan_tetrahedron.py +++ b/symfem/elements/_guzman_neilan_tetrahedron.py @@ -4,68 +4,191 @@ coeffs = [ [ - sympy.Rational(5, 8), sympy.Rational(5, 8), sympy.Rational(5, 8), 0, 0, - sympy.Rational(125, 72), sympy.Rational(17, 36), sympy.Rational(17, 36), - sympy.Rational(71, 18), sympy.Rational(-6343, 81000), sympy.Rational(-16157, 81000), - sympy.Rational(325, 216), sympy.Rational(6391, 20250), sympy.Rational(742, 10125), - sympy.Rational(58, 27), sympy.Rational(-7879, 10125), sympy.Rational(325, 216), - sympy.Rational(-100093, 81000), sympy.Rational(-43907, 40500), sympy.Rational(58, 27), - sympy.Rational(-20242, 10125), sympy.Rational(325, 216), sympy.Rational(-26609, 40500), - sympy.Rational(-109907, 81000), sympy.Rational(58, 27), sympy.Rational(-34093, 40500), - sympy.Rational(-45391, 20250), sympy.Rational(-13471, 54000), sympy.Rational(15971, 54000), - sympy.Rational(-25, 27), sympy.Rational(-7879, 20250), sympy.Rational(-545, 144), - sympy.Rational(10133, 40500), sympy.Rational(-545, 144), sympy.Rational(-26609, 81000), - sympy.Rational(15359, 81000), sympy.Rational(39149, 13500), sympy.Rational(-25, 432), - sympy.Rational(-9631, 3375), sympy.Rational(-6343, 162000), sympy.Rational(-16157, 162000), - sympy.Rational(145, 36), sympy.Rational(-25, 432), sympy.Rational(63577, 27000), + sympy.Rational(5, 8), + sympy.Rational(5, 8), + sympy.Rational(5, 8), + 0, + 0, + sympy.Rational(125, 72), + sympy.Rational(17, 36), + sympy.Rational(17, 36), + sympy.Rational(71, 18), + sympy.Rational(-6343, 81000), + sympy.Rational(-16157, 81000), + sympy.Rational(325, 216), + sympy.Rational(6391, 20250), + sympy.Rational(742, 10125), + sympy.Rational(58, 27), + sympy.Rational(-7879, 10125), + sympy.Rational(325, 216), + sympy.Rational(-100093, 81000), + sympy.Rational(-43907, 40500), + sympy.Rational(58, 27), + sympy.Rational(-20242, 10125), + sympy.Rational(325, 216), + sympy.Rational(-26609, 40500), + sympy.Rational(-109907, 81000), + sympy.Rational(58, 27), + sympy.Rational(-34093, 40500), + sympy.Rational(-45391, 20250), + sympy.Rational(-13471, 54000), + sympy.Rational(15971, 54000), + sympy.Rational(-25, 27), + sympy.Rational(-7879, 20250), + sympy.Rational(-545, 144), + sympy.Rational(10133, 40500), + sympy.Rational(-545, 144), + sympy.Rational(-26609, 81000), + sympy.Rational(15359, 81000), + sympy.Rational(39149, 13500), + sympy.Rational(-25, 432), + sympy.Rational(-9631, 3375), + sympy.Rational(-6343, 162000), + sympy.Rational(-16157, 162000), + sympy.Rational(145, 36), + sympy.Rational(-25, 432), + sympy.Rational(63577, 27000), sympy.Rational(-62327, 27000), ], [ - sympy.Rational(15, 8), 0, 0, 0, sympy.Rational(125, 72), 0, sympy.Rational(1, 12), - sympy.Rational(113, 36), sympy.Rational(-1, 3), sympy.Rational(-10921, 8100), - sympy.Rational(27467, 16200), sympy.Rational(-25, 18), sympy.Rational(-21167, 8100), - sympy.Rational(24767, 8100), sympy.Rational(-16, 9), sympy.Rational(25217, 16200), - sympy.Rational(-25, 18), sympy.Rational(8533, 16200), sympy.Rational(6473, 2025), - sympy.Rational(-16, 9), sympy.Rational(5833, 8100), sympy.Rational(125, 72), - sympy.Rational(-8273, 4050), sympy.Rational(13967, 16200), sympy.Rational(44, 9), - sympy.Rational(-8948, 2025), sympy.Rational(11267, 8100), sympy.Rational(9671, 1800), - sympy.Rational(-2449, 450), sympy.Rational(25, 36), sympy.Rational(25217, 32400), - sympy.Rational(235, 48), sympy.Rational(8533, 32400), sympy.Rational(-235, 144), - sympy.Rational(-4967, 32400), sympy.Rational(13967, 32400), sympy.Rational(-9239, 1200), - sympy.Rational(-25, 144), sympy.Rational(27467, 3600), sympy.Rational(-10921, 16200), - sympy.Rational(6949, 4050), sympy.Rational(-35, 12), sympy.Rational(-145, 144), - sympy.Rational(12967, 3600), sympy.Rational(-663, 400), + sympy.Rational(15, 8), + 0, + 0, + 0, + sympy.Rational(125, 72), + 0, + sympy.Rational(1, 12), + sympy.Rational(113, 36), + sympy.Rational(-1, 3), + sympy.Rational(-10921, 8100), + sympy.Rational(27467, 16200), + sympy.Rational(-25, 18), + sympy.Rational(-21167, 8100), + sympy.Rational(24767, 8100), + sympy.Rational(-16, 9), + sympy.Rational(25217, 16200), + sympy.Rational(-25, 18), + sympy.Rational(8533, 16200), + sympy.Rational(6473, 2025), + sympy.Rational(-16, 9), + sympy.Rational(5833, 8100), + sympy.Rational(125, 72), + sympy.Rational(-8273, 4050), + sympy.Rational(13967, 16200), + sympy.Rational(44, 9), + sympy.Rational(-8948, 2025), + sympy.Rational(11267, 8100), + sympy.Rational(9671, 1800), + sympy.Rational(-2449, 450), + sympy.Rational(25, 36), + sympy.Rational(25217, 32400), + sympy.Rational(235, 48), + sympy.Rational(8533, 32400), + sympy.Rational(-235, 144), + sympy.Rational(-4967, 32400), + sympy.Rational(13967, 32400), + sympy.Rational(-9239, 1200), + sympy.Rational(-25, 144), + sympy.Rational(27467, 3600), + sympy.Rational(-10921, 16200), + sympy.Rational(6949, 4050), + sympy.Rational(-35, 12), + sympy.Rational(-145, 144), + sympy.Rational(12967, 3600), + sympy.Rational(-663, 400), ], [ - 0, sympy.Rational(-15, 8), 0, 0, sympy.Rational(-61387, 81000), - sympy.Rational(-39619, 40500), sympy.Rational(1, 3), sympy.Rational(-32381, 20250), - sympy.Rational(-32869, 20250), sympy.Rational(-37, 18), sympy.Rational(59137, 81000), - sympy.Rational(25, 18), sympy.Rational(-34, 9), sympy.Rational(27881, 20250), - sympy.Rational(16, 9), sympy.Rational(2, 3), sympy.Rational(-125, 72), - sympy.Rational(-9881, 40500), sympy.Rational(5, 3), sympy.Rational(-44, 9), - sympy.Rational(-3131, 20250), sympy.Rational(25, 18), sympy.Rational(-13, 72), - sympy.Rational(-1, 6), sympy.Rational(16, 9), sympy.Rational(-4, 9), 0, - sympy.Rational(-3, 4), sympy.Rational(59, 72), sympy.Rational(-16631, 81000), - sympy.Rational(1, 3), sympy.Rational(-8003, 4500), sympy.Rational(-11, 18), - sympy.Rational(35, 12), sympy.Rational(-19003, 40500), sympy.Rational(-46369, 81000), - sympy.Rational(-47, 9), sympy.Rational(14032, 10125), sympy.Rational(59, 18), - sympy.Rational(-37, 36), sympy.Rational(-1, 72), sympy.Rational(-13369, 9000), - sympy.Rational(-25, 36), sympy.Rational(1844, 375), sympy.Rational(-43631, 9000), + 0, + sympy.Rational(-15, 8), + 0, + 0, + sympy.Rational(-61387, 81000), + sympy.Rational(-39619, 40500), + sympy.Rational(1, 3), + sympy.Rational(-32381, 20250), + sympy.Rational(-32869, 20250), + sympy.Rational(-37, 18), + sympy.Rational(59137, 81000), + sympy.Rational(25, 18), + sympy.Rational(-34, 9), + sympy.Rational(27881, 20250), + sympy.Rational(16, 9), + sympy.Rational(2, 3), + sympy.Rational(-125, 72), + sympy.Rational(-9881, 40500), + sympy.Rational(5, 3), + sympy.Rational(-44, 9), + sympy.Rational(-3131, 20250), + sympy.Rational(25, 18), + sympy.Rational(-13, 72), + sympy.Rational(-1, 6), + sympy.Rational(16, 9), + sympy.Rational(-4, 9), + 0, + sympy.Rational(-3, 4), + sympy.Rational(59, 72), + sympy.Rational(-16631, 81000), + sympy.Rational(1, 3), + sympy.Rational(-8003, 4500), + sympy.Rational(-11, 18), + sympy.Rational(35, 12), + sympy.Rational(-19003, 40500), + sympy.Rational(-46369, 81000), + sympy.Rational(-47, 9), + sympy.Rational(14032, 10125), + sympy.Rational(59, 18), + sympy.Rational(-37, 36), + sympy.Rational(-1, 72), + sympy.Rational(-13369, 9000), + sympy.Rational(-25, 36), + sympy.Rational(1844, 375), + sympy.Rational(-43631, 9000), ], [ - 0, 0, sympy.Rational(15, 8), sympy.Rational(108281, 40500), 0, - sympy.Rational(-75937, 81000), sympy.Rational(101531, 20250), sympy.Rational(-1, 3), - sympy.Rational(-36281, 20250), sympy.Rational(-132187, 81000), sympy.Rational(-35, 72), - sympy.Rational(125, 72), sympy.Rational(-145687, 40500), sympy.Rational(-47, 36), - sympy.Rational(44, 9), sympy.Rational(25, 72), sympy.Rational(-25, 18), 0, - sympy.Rational(13, 36), sympy.Rational(-16, 9), sympy.Rational(1, 12), - sympy.Rational(-25, 18), sympy.Rational(15, 8), sympy.Rational(23203, 20250), - sympy.Rational(-16, 9), sympy.Rational(41, 12), sympy.Rational(96187, 40500), - sympy.Rational(-355, 144), sympy.Rational(635, 144), sympy.Rational(-21797, 40500), - sympy.Rational(244687, 162000), sympy.Rational(-35, 12), sympy.Rational(-75937, 162000), - sympy.Rational(82031, 9000), sympy.Rational(15, 16), sympy.Rational(5, 48), - sympy.Rational(-64687, 18000), sympy.Rational(25, 36), sympy.Rational(63437, 18000), - sympy.Rational(25, 48), sympy.Rational(-35, 144), sympy.Rational(-2194, 375), - sympy.Rational(-52031, 81000), sympy.Rational(25, 16), sympy.Rational(-235, 144), + 0, + 0, + sympy.Rational(15, 8), + sympy.Rational(108281, 40500), + 0, + sympy.Rational(-75937, 81000), + sympy.Rational(101531, 20250), + sympy.Rational(-1, 3), + sympy.Rational(-36281, 20250), + sympy.Rational(-132187, 81000), + sympy.Rational(-35, 72), + sympy.Rational(125, 72), + sympy.Rational(-145687, 40500), + sympy.Rational(-47, 36), + sympy.Rational(44, 9), + sympy.Rational(25, 72), + sympy.Rational(-25, 18), + 0, + sympy.Rational(13, 36), + sympy.Rational(-16, 9), + sympy.Rational(1, 12), + sympy.Rational(-25, 18), + sympy.Rational(15, 8), + sympy.Rational(23203, 20250), + sympy.Rational(-16, 9), + sympy.Rational(41, 12), + sympy.Rational(96187, 40500), + sympy.Rational(-355, 144), + sympy.Rational(635, 144), + sympy.Rational(-21797, 40500), + sympy.Rational(244687, 162000), + sympy.Rational(-35, 12), + sympy.Rational(-75937, 162000), + sympy.Rational(82031, 9000), + sympy.Rational(15, 16), + sympy.Rational(5, 48), + sympy.Rational(-64687, 18000), + sympy.Rational(25, 36), + sympy.Rational(63437, 18000), + sympy.Rational(25, 48), + sympy.Rational(-35, 144), + sympy.Rational(-2194, 375), + sympy.Rational(-52031, 81000), + sympy.Rational(25, 16), + sympy.Rational(-235, 144), ], ] diff --git a/symfem/elements/_guzman_neilan_triangle.py b/symfem/elements/_guzman_neilan_triangle.py index 7bb4e20b..58e8da0f 100644 --- a/symfem/elements/_guzman_neilan_triangle.py +++ b/symfem/elements/_guzman_neilan_triangle.py @@ -4,15 +4,33 @@ coeffs = [ [ - sympy.Rational(1, 6), sympy.Rational(1, 6), sympy.Rational(5, 24), sympy.Rational(-1, 24), - sympy.Rational(5, 24), sympy.Rational(5, 24), sympy.Rational(-1, 24), sympy.Rational(5, 24), + sympy.Rational(1, 6), + sympy.Rational(1, 6), + sympy.Rational(5, 24), + sympy.Rational(-1, 24), + sympy.Rational(5, 24), + sympy.Rational(5, 24), + sympy.Rational(-1, 24), + sympy.Rational(5, 24), ], [ - sympy.Rational(1, 3), sympy.Rational(-2, 3), sympy.Rational(5, 12), sympy.Rational(-1, 3), - sympy.Rational(-1, 12), sympy.Rational(-1, 3), sympy.Rational(5, 12), sympy.Rational(-5, 6), + sympy.Rational(1, 3), + sympy.Rational(-2, 3), + sympy.Rational(5, 12), + sympy.Rational(-1, 3), + sympy.Rational(-1, 12), + sympy.Rational(-1, 3), + sympy.Rational(5, 12), + sympy.Rational(-5, 6), ], [ - sympy.Rational(2, 3), sympy.Rational(-1, 3), sympy.Rational(5, 6), sympy.Rational(-5, 12), - sympy.Rational(1, 3), sympy.Rational(1, 12), sympy.Rational(1, 3), sympy.Rational(-5, 12), + sympy.Rational(2, 3), + sympy.Rational(-1, 3), + sympy.Rational(5, 6), + sympy.Rational(-5, 12), + sympy.Rational(1, 3), + sympy.Rational(1, 12), + sympy.Rational(1, 3), + sympy.Rational(-5, 12), ], ] diff --git a/symfem/elements/abf.py b/symfem/elements/abf.py index 765b2244..1801ea5b 100644 --- a/symfem/elements/abf.py +++ b/symfem/elements/abf.py @@ -7,8 +7,12 @@ import typing from ..finite_element import CiarletElement -from ..functionals import (IntegralMoment, IntegralOfDivergenceAgainst, ListOfFunctionals, - NormalIntegralMoment) +from ..functionals import ( + IntegralMoment, + IntegralOfDivergenceAgainst, + ListOfFunctionals, + NormalIntegralMoment, +) from ..functions import FunctionInput from ..moments import make_integral_moment_dofs from ..references import NonDefaultReferenceError, Reference @@ -33,25 +37,32 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" raise NonDefaultReferenceError() poly: typing.List[FunctionInput] = [] - poly += [ - (x[0] ** i * x[1] ** j, 0) - for i in range(order + 3) for j in range(order + 1)] - poly += [(0, x[0] ** i * x[1] ** j) - for i in range(order + 1) for j in range(order + 3)] + poly += [(x[0] ** i * x[1] ** j, 0) for i in range(order + 3) for j in range(order + 1)] + poly += [(0, x[0] ** i * x[1] ** j) for i in range(order + 1) for j in range(order + 3)] dofs: ListOfFunctionals = make_integral_moment_dofs( reference, edges=(NormalIntegralMoment, Lagrange, order, {"variant": variant}), - faces=(IntegralMoment, Nedelec, order, {"variant": variant}) + faces=(IntegralMoment, Nedelec, order, {"variant": variant}), ) for i in range(order + 1): - dofs.append(IntegralOfDivergenceAgainst( - reference, x[0] ** (order + 1) * x[1] ** i, - entity=(2, 0), mapping="contravariant")) - dofs.append(IntegralOfDivergenceAgainst( - reference, x[0] ** i * x[1] ** (order + 1), - entity=(2, 0), mapping="contravariant")) + dofs.append( + IntegralOfDivergenceAgainst( + reference, + x[0] ** (order + 1) * x[1] ** i, + entity=(2, 0), + mapping="contravariant", + ) + ) + dofs.append( + IntegralOfDivergenceAgainst( + reference, + x[0] ** i * x[1] ** (order + 1), + entity=(2, 0), + mapping="contravariant", + ) + ) super().__init__(reference, order, poly, dofs, reference.tdim, reference.tdim) self.variant = variant diff --git a/symfem/elements/ac.py b/symfem/elements/ac.py index c2a674b8..2dc408ac 100644 --- a/symfem/elements/ac.py +++ b/symfem/elements/ac.py @@ -36,8 +36,10 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" poly += [(x[0], 0), (0, x[1])] else: poly += Hdiv_serendipity(reference.tdim, reference.tdim, order) - poly += [(x[0] ** (i + 1) * x[1] ** (order - i), x[0] ** i * x[1] ** (1 + order - i)) - for i in range(order + 1)] + poly += [ + (x[0] ** (i + 1) * x[1] ** (order - i), x[0] ** i * x[1] ** (1 + order - i)) + for i in range(order + 1) + ] dofs: ListOfFunctionals = make_integral_moment_dofs( reference, @@ -48,17 +50,17 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" for j in range(order + 1 - i): if i + j > 0: f = (i * x[0] ** (i - 1) * x[1] ** j, j * x[0] ** i * x[1] ** (j - 1)) - dofs.append(IntegralAgainst(reference, f, entity=(2, 0), - mapping="contravariant")) + dofs.append( + IntegralAgainst(reference, f, entity=(2, 0), mapping="contravariant") + ) for i in range(1, order - 1): for j in range(1, order - i): f = ( x[0] ** i * (1 - x[0]) * x[1] ** (j - 1) * (j * (1 - x[1]) - x[1]), - -x[0] ** (i - 1) * (i * (1 - x[0]) - x[0]) * x[1] ** j * (1 - x[1]) + -(x[0] ** (i - 1)) * (i * (1 - x[0]) - x[0]) * x[1] ** j * (1 - x[1]), ) - dofs.append(IntegralAgainst(reference, f, entity=(2, 0), - mapping="contravariant")) + dofs.append(IntegralAgainst(reference, f, entity=(2, 0), mapping="contravariant")) super().__init__(reference, order, poly, dofs, reference.tdim, reference.tdim) self.variant = variant diff --git a/symfem/elements/alfeld_sorokina.py b/symfem/elements/alfeld_sorokina.py index 26f603eb..607fab5d 100644 --- a/symfem/elements/alfeld_sorokina.py +++ b/symfem/elements/alfeld_sorokina.py @@ -47,40 +47,81 @@ def __init__(self, reference: Reference, order: int): subs = [ (reference.vertices[0], reference.vertices[1], mid), (reference.vertices[1], reference.vertices[2], mid), - (reference.vertices[2], reference.vertices[0], mid)] - - piece_list = [tuple(VectorFunction((p, 0)) for _ in range(3)) - for p in [1, x[0], x[1], x[0]**2, x[0]*x[1], x[1]**2]] - piece_list += [tuple(VectorFunction((0, p)) for _ in range(3)) - for p in [1, x[0], x[1], x[0]**2, x[0]*x[1], x[1]**2]] - - piece_list.append(( - VectorFunction((4*x[0] + 12*x[1]**2 - 4*x[1], 6*x[0]*x[1] - 4*x[1])), - VectorFunction((3*x[0]**2 + 2*x[0] + 4*x[1] - 1, -3*x[0]**2 + 4*x[0] - 2*x[1] - 1)), - VectorFunction((3*x[0]**2 + 6*x[0]*x[1] + 3*x[1]**2, 9*x[0]**2 - 4*x[0] - 3*x[1]**2)))) - piece_list.append(( - VectorFunction((144*x[0]*x[1] + 10*x[0] + 12*x[1]**2 - 10*x[1], - -30*x[0]*x[1] + 36*x[1]**2 - 10*x[1])), - VectorFunction((-69*x[0]**2 + 104*x[0] + 46*x[1] - 25, - 24*x[0]**2 - 26*x[0] + 4*x[1] + 2)), - VectorFunction((39*x[0]**2 + 96*x[0]*x[1] + 21*x[1]**2, - -10*x[0] + 6*x[1]**2)))) - piece_list.append(( - VectorFunction((-14*x[0] + 12*x[1]**2 + 14*x[1], - 42*x[0]*x[1] - 108*x[1]**2 + 14*x[1])), - VectorFunction((3*x[0]**2 - 16*x[0] + 22*x[1] - 1, - 24*x[0]**2 + 144*x[0]*x[1] - 50*x[0] - 92*x[1] + 26)), - VectorFunction((-33*x[0]**2 + 24*x[0]*x[1] + 21*x[1]**2, - 14*x[0] - 66*x[1]**2)))) + (reference.vertices[2], reference.vertices[0], mid), + ] + + piece_list = [ + tuple(VectorFunction((p, 0)) for _ in range(3)) + for p in [1, x[0], x[1], x[0] ** 2, x[0] * x[1], x[1] ** 2] + ] + piece_list += [ + tuple(VectorFunction((0, p)) for _ in range(3)) + for p in [1, x[0], x[1], x[0] ** 2, x[0] * x[1], x[1] ** 2] + ] + + piece_list.append( + ( + VectorFunction((4 * x[0] + 12 * x[1] ** 2 - 4 * x[1], 6 * x[0] * x[1] - 4 * x[1])), + VectorFunction( + ( + 3 * x[0] ** 2 + 2 * x[0] + 4 * x[1] - 1, + -3 * x[0] ** 2 + 4 * x[0] - 2 * x[1] - 1, + ) + ), + VectorFunction( + ( + 3 * x[0] ** 2 + 6 * x[0] * x[1] + 3 * x[1] ** 2, + 9 * x[0] ** 2 - 4 * x[0] - 3 * x[1] ** 2, + ) + ), + ) + ) + piece_list.append( + ( + VectorFunction( + ( + 144 * x[0] * x[1] + 10 * x[0] + 12 * x[1] ** 2 - 10 * x[1], + -30 * x[0] * x[1] + 36 * x[1] ** 2 - 10 * x[1], + ) + ), + VectorFunction( + ( + -69 * x[0] ** 2 + 104 * x[0] + 46 * x[1] - 25, + 24 * x[0] ** 2 - 26 * x[0] + 4 * x[1] + 2, + ) + ), + VectorFunction( + (39 * x[0] ** 2 + 96 * x[0] * x[1] + 21 * x[1] ** 2, -10 * x[0] + 6 * x[1] ** 2) + ), + ) + ) + piece_list.append( + ( + VectorFunction( + ( + -14 * x[0] + 12 * x[1] ** 2 + 14 * x[1], + 42 * x[0] * x[1] - 108 * x[1] ** 2 + 14 * x[1], + ) + ), + VectorFunction( + ( + 3 * x[0] ** 2 - 16 * x[0] + 22 * x[1] - 1, + 24 * x[0] ** 2 + 144 * x[0] * x[1] - 50 * x[0] - 92 * x[1] + 26, + ) + ), + VectorFunction( + ( + -33 * x[0] ** 2 + 24 * x[0] * x[1] + 21 * x[1] ** 2, + 14 * x[0] - 66 * x[1] ** 2, + ) + ), + ) + ) poly: typing.List[FunctionInput] = [] - poly += [ - PiecewiseFunction({i: j for i, j in zip(subs, p)}, 2) - for p in piece_list] + poly += [PiecewiseFunction({i: j for i, j in zip(subs, p)}, 2) for p in piece_list] - super().__init__( - reference, order, poly, dofs, reference.tdim, 1 - ) + super().__init__(reference, order, poly, dofs, reference.tdim, 1) names = ["Alfeld-Sorokina", "AS"] references = ["triangle"] diff --git a/symfem/elements/argyris.py b/symfem/elements/argyris.py index cb24b4dc..f4d6f289 100644 --- a/symfem/elements/argyris.py +++ b/symfem/elements/argyris.py @@ -7,9 +7,13 @@ import typing from ..finite_element import CiarletElement -from ..functionals import (ListOfFunctionals, PointComponentSecondDerivativeEvaluation, - PointDirectionalDerivativeEvaluation, PointEvaluation, - PointNormalDerivativeEvaluation) +from ..functionals import ( + ListOfFunctionals, + PointComponentSecondDerivativeEvaluation, + PointDirectionalDerivativeEvaluation, + PointEvaluation, + PointNormalDerivativeEvaluation, +) from ..functions import FunctionInput from ..polynomials import polynomial_set_1d from ..references import NonDefaultReferenceError, Reference @@ -35,17 +39,24 @@ def __init__(self, reference: Reference, order: int): dofs.append(PointEvaluation(reference, vs, entity=(0, v_n))) for i in range(reference.tdim): direction = tuple(1 if i == j else 0 for j in range(reference.tdim)) - dofs.append(PointDirectionalDerivativeEvaluation( - reference, vs, direction, entity=(0, v_n))) + dofs.append( + PointDirectionalDerivativeEvaluation(reference, vs, direction, entity=(0, v_n)) + ) for i in range(reference.tdim): for j in range(i + 1): - dofs.append(PointComponentSecondDerivativeEvaluation( - reference, vs, (i, j), entity=(0, v_n))) + dofs.append( + PointComponentSecondDerivativeEvaluation( + reference, vs, (i, j), entity=(0, v_n) + ) + ) for e_n in range(reference.sub_entity_count(1)): assert isinstance(reference.sub_entity_types[1], str) sub_ref = reference.sub_entity(1, e_n) - dofs.append(PointNormalDerivativeEvaluation( - reference, sub_ref.midpoint(), sub_ref, entity=(1, e_n))) + dofs.append( + PointNormalDerivativeEvaluation( + reference, sub_ref.midpoint(), sub_ref, entity=(1, e_n) + ) + ) poly: typing.List[FunctionInput] = [] poly += polynomial_set_1d(reference.tdim, order) diff --git a/symfem/elements/aw.py b/symfem/elements/aw.py index df9b24de..0297b808 100644 --- a/symfem/elements/aw.py +++ b/symfem/elements/aw.py @@ -10,8 +10,12 @@ import sympy from ..finite_element import CiarletElement -from ..functionals import (InnerProductIntegralMoment, IntegralMoment, ListOfFunctionals, - PointInnerProduct) +from ..functionals import ( + InnerProductIntegralMoment, + IntegralMoment, + ListOfFunctionals, + PointInnerProduct, +) from ..functions import FunctionInput from ..polynomials import polynomial_set_vector from ..references import Reference @@ -35,22 +39,34 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" poly: typing.List[FunctionInput] = [] poly += [ ((p[0], p[1]), (p[1], p[2])) - for p in polynomial_set_vector(reference.tdim, 3, order - 1)] - poly += [(((order - k + 1) * (order - k + 2) * x[0] ** k * x[1] ** (order - k), - -k * (order - k + 2) * x[0] ** (k - 1) * x[1] ** (order - k + 1)), - (-k * (order - k + 2) * x[0] ** (k - 1) * x[1] ** (order - k + 1), - -k * (k - 1) * x[0] ** (k - 2) * x[1] ** (order - k + 2))) - for k in range(order + 1)] - poly += [((0, x[0] ** order), (x[0] ** order, -order * x[0] ** (order - 1) * x[1])), - ((0, 0), (0, x[0] ** order))] + for p in polynomial_set_vector(reference.tdim, 3, order - 1) + ] + poly += [ + ( + ( + (order - k + 1) * (order - k + 2) * x[0] ** k * x[1] ** (order - k), + -k * (order - k + 2) * x[0] ** (k - 1) * x[1] ** (order - k + 1), + ), + ( + -k * (order - k + 2) * x[0] ** (k - 1) * x[1] ** (order - k + 1), + -k * (k - 1) * x[0] ** (k - 2) * x[1] ** (order - k + 2), + ), + ) + for k in range(order + 1) + ] + poly += [ + ((0, x[0] ** order), (x[0] ** order, -order * x[0] ** (order - 1) * x[1])), + ((0, 0), (0, x[0] ** order)), + ] dofs: ListOfFunctionals = [] for v_n, v in enumerate(reference.vertices): - for d in [[(1, 0), (1, 0)], - [(1, 0), (0, 1)], - [(0, 1), (0, 1)]]: - dofs.append(PointInnerProduct(reference, v, d[0], d[1], entity=(0, v_n), - mapping="double_contravariant")) + for d in [[(1, 0), (1, 0)], [(1, 0), (0, 1)], [(0, 1), (0, 1)]]: + dofs.append( + PointInnerProduct( + reference, v, d[0], d[1], entity=(0, v_n), mapping="double_contravariant" + ) + ) for e_n in range(reference.sub_entity_count(1)): sub_ref = reference.sub_entity(1, e_n) sub_e = Lagrange(sub_ref.default_reference(), order - 2, variant) @@ -58,19 +74,37 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" p = sub_e.get_basis_function(dof_n).get_function() for component in [sub_ref.normal(), sub_ref.tangent()]: InnerProductIntegralMoment( - reference, p, component, sub_ref.normal(), dof, - entity=(1, e_n), mapping="double_contravariant") + reference, + p, + component, + sub_ref.normal(), + dof, + entity=(1, e_n), + mapping="double_contravariant", + ) dofs.append( InnerProductIntegralMoment( - reference, p, component, sub_ref.normal(), dof, - entity=(1, e_n), mapping="double_contravariant")) + reference, + p, + component, + sub_ref.normal(), + dof, + entity=(1, e_n), + mapping="double_contravariant", + ) + ) sub_e = Lagrange(reference, order - 3, variant) for dof_n, dof in enumerate(sub_e.dofs): p = sub_e.get_basis_function(dof_n).get_function() for component22 in [((1, 0), (0, 0)), ((0, 1), (0, 0)), ((0, 0), (0, 1))]: - dofs.append(IntegralMoment( - reference, tuple(tuple(p * j for j in i) for i in component22), - dof, entity=(2, 0))) + dofs.append( + IntegralMoment( + reference, + tuple(tuple(p * j for j in i) for i in component22), + dof, + entity=(2, 0), + ) + ) if order >= 4: sub_e = Lagrange(reference, order - 4, variant) @@ -78,12 +112,18 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" if sympy.Poly(p.as_sympy(), x[:2]).degree() != order - 4: continue f = p * x[0] ** 2 * x[1] ** 2 * (1 - x[0] - x[1]) ** 2 - J = tuple(tuple(f.diff(x[i]).diff(x[j]) for j in range(2)) - for i in range(2)) + J = tuple(tuple(f.diff(x[i]).diff(x[j]) for j in range(2)) for i in range(2)) dofs.append(IntegralMoment(reference, J, dof, entity=(2, 0))) - super().__init__(reference, order, poly, dofs, reference.tdim, reference.tdim ** 2, - (reference.tdim, reference.tdim)) + super().__init__( + reference, + order, + poly, + dofs, + reference.tdim, + reference.tdim**2, + (reference.tdim, reference.tdim), + ) def init_kwargs(self) -> typing.Dict[str, typing.Any]: """Return the kwargs used to create this element. @@ -116,7 +156,8 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" poly: typing.List[FunctionInput] = [] poly += [ ((p[0], p[1]), (p[1], p[2])) - for p in polynomial_set_vector(reference.tdim, 3, order - 1)] + for p in polynomial_set_vector(reference.tdim, 3, order - 1) + ] poly += [ ((0, x[1] ** 2), (x[1] ** 2, -2 * x[1] ** 2)), @@ -124,7 +165,7 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" ((-2 * x[0] * x[1], x[0] * x[1]), (x[0] * x[1], 0)), ((x[0] * (x[0] - x[1]), 0), (0, 0)), ((x[0] ** 2, 0), (0, x[0] * x[1])), - ((x[0] ** 2, 0), (0, x[1] ** 2)) + ((x[0] ** 2, 0), (0, x[1] ** 2)), ] dofs: ListOfFunctionals = [] @@ -136,18 +177,37 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" for component in [sub_ref.normal(), sub_ref.tangent()]: dofs.append( InnerProductIntegralMoment( - reference, p, component, sub_ref.normal(), dof, - entity=(1, e_n), mapping="double_contravariant")) + reference, + p, + component, + sub_ref.normal(), + dof, + entity=(1, e_n), + mapping="double_contravariant", + ) + ) sub_e = Lagrange(reference, 0, variant) for dof_n, dof in enumerate(sub_e.dofs): p = sub_e.get_basis_function(dof_n).get_function() for component22 in [((1, 0), (0, 0)), ((0, 1), (0, 0)), ((0, 0), (0, 1))]: - dofs.append(IntegralMoment( - reference, tuple(tuple(p * j for j in i) for i in component22), - dof, entity=(2, 0))) - - super().__init__(reference, order, poly, dofs, reference.tdim, reference.tdim ** 2, - (reference.tdim, reference.tdim)) + dofs.append( + IntegralMoment( + reference, + tuple(tuple(p * j for j in i) for i in component22), + dof, + entity=(2, 0), + ) + ) + + super().__init__( + reference, + order, + poly, + dofs, + reference.tdim, + reference.tdim**2, + (reference.tdim, reference.tdim), + ) def init_kwargs(self) -> typing.Dict[str, typing.Any]: """Return the kwargs used to create this element. diff --git a/symfem/elements/bddm.py b/symfem/elements/bddm.py index 45f30b02..75c9aeca 100644 --- a/symfem/elements/bddm.py +++ b/symfem/elements/bddm.py @@ -61,7 +61,7 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" dofs: ListOfFunctionals = make_integral_moment_dofs( reference, facets=(NormalIntegralMoment, DPC, order, {"variant": variant}), - cells=(IntegralMoment, VectorDPC, order - 2, {"variant": variant}) + cells=(IntegralMoment, VectorDPC, order - 2, {"variant": variant}), ) self.variant = variant diff --git a/symfem/elements/bell.py b/symfem/elements/bell.py index 6c4bba8a..0ce87ff0 100644 --- a/symfem/elements/bell.py +++ b/symfem/elements/bell.py @@ -38,9 +38,9 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" poly: typing.List[FunctionInput] = [] poly += polynomial_set_1d(reference.tdim, 4) - poly.append(x[0]**5 - x[1]**5) - poly.append(x[0]**3*x[1]**2 - x[0]**2*x[1]**3) - poly.append(5*x[0]**2*x[1]**3 - x[0]**5) + poly.append(x[0] ** 5 - x[1] ** 5) + poly.append(x[0] ** 3 * x[1] ** 2 - x[0] ** 2 * x[1] ** 3) + poly.append(5 * x[0] ** 2 * x[1] ** 3 - x[0] ** 5) super().__init__(reference, order, poly, dofs, reference.tdim, 1) diff --git a/symfem/elements/bernardi_raugel.py b/symfem/elements/bernardi_raugel.py index 65bb33a6..8b34af36 100644 --- a/symfem/elements/bernardi_raugel.py +++ b/symfem/elements/bernardi_raugel.py @@ -9,8 +9,12 @@ import sympy from ..finite_element import CiarletElement -from ..functionals import (DivergenceIntegralMoment, DotPointEvaluation, ListOfFunctionals, - NormalIntegralMoment) +from ..functionals import ( + DivergenceIntegralMoment, + DotPointEvaluation, + ListOfFunctionals, + NormalIntegralMoment, +) from ..functions import FunctionInput from ..moments import make_integral_moment_dofs from ..polynomials import polynomial_set_vector @@ -50,14 +54,19 @@ def __init__(self, reference: Reference, order: int): for n in range(reference.sub_entity_count(reference.tdim - 1)): facet = reference.sub_entity(reference.tdim - 1, n) for v in facet.vertices: - dofs.append(DotPointEvaluation( - reference, v, tuple(i * facet.jacobian() for i in facet.normal()), - entity=(reference.tdim - 1, n), mapping="contravariant")) + dofs.append( + DotPointEvaluation( + reference, + v, + tuple(i * facet.jacobian() for i in facet.normal()), + entity=(reference.tdim - 1, n), + mapping="contravariant", + ) + ) dofs += make_integral_moment_dofs( reference, - facets=(NormalIntegralMoment, Lagrange, 0, "contravariant", - {"variant": "equispaced"}), + facets=(NormalIntegralMoment, Lagrange, 0, "contravariant", {"variant": "equispaced"}), ) if order > 1: @@ -65,33 +74,45 @@ def __init__(self, reference: Reference, order: int): for i in range(reference.tdim): bf = p.get_basis_functions() - poly.append(tuple( - bf[0] * bf[1] * bf[2] * bf[3] if j == i else 0 for j in range(reference.tdim))) + poly.append( + tuple( + bf[0] * bf[1] * bf[2] * bf[3] if j == i else 0 + for j in range(reference.tdim) + ) + ) for e_n, edge in enumerate(reference.edges): v1 = reference.vertices[edge[0]] v2 = reference.vertices[edge[1]] midpoint = tuple(sympy.Rational(i + j, 2) for i, j in zip(v1, v2)) d = tuple(j - i for i, j in zip(v1, v2)) - dofs.append(DotPointEvaluation(reference, midpoint, d, entity=(1, e_n), - mapping="contravariant")) + dofs.append( + DotPointEvaluation( + reference, midpoint, d, entity=(1, e_n), mapping="contravariant" + ) + ) for f_n in range(reference.sub_entity_count(2)): face = reference.sub_entity(2, f_n) normal = tuple(i * face.jacobian() for i in face.normal()) for e_n in range(3): edge_entity = face.sub_entity(1, e_n) midpoint = tuple( - sympy.Rational(i + j, 2) for i, j in zip(*edge_entity.vertices)) - dofs.append(DotPointEvaluation( - reference, midpoint, normal, entity=(2, f_n), mapping="contravariant")) + sympy.Rational(i + j, 2) for i, j in zip(*edge_entity.vertices) + ) + dofs.append( + DotPointEvaluation( + reference, midpoint, normal, entity=(2, f_n), mapping="contravariant" + ) + ) p = Lagrange(reference, 0, variant="equispaced") for i in range(3): - dofs.append(DivergenceIntegralMoment( - reference, x[i], p.dofs[0], entity=(3, 0), - mapping="contravariant" - )) + dofs.append( + DivergenceIntegralMoment( + reference, x[i], p.dofs[0], entity=(3, 0), mapping="contravariant" + ) + ) super().__init__(reference, order, poly, dofs, reference.tdim, reference.tdim) diff --git a/symfem/elements/bernstein.py b/symfem/elements/bernstein.py index 0a348215..0d981e9f 100644 --- a/symfem/elements/bernstein.py +++ b/symfem/elements/bernstein.py @@ -72,20 +72,20 @@ def bernstein_polynomials( powers = [[n - i, i] for i in range(n + 1)] elif d == 2: lambdas = [1 - vars[0] - vars[1], vars[0], vars[1]] - powers = [[n - i - j, j, i] - for i in range(n + 1) - for j in range(n + 1 - i)] + powers = [[n - i - j, j, i] for i in range(n + 1) for j in range(n + 1 - i)] elif d == 3: lambdas = [1 - vars[0] - vars[1] - vars[2], vars[0], vars[1], vars[2]] - powers = [[n - i - j - k, k, j, i] - for i in range(n + 1) - for j in range(n + 1 - i) - for k in range(n + 1 - i - j)] + powers = [ + [n - i - j - k, k, j, i] + for i in range(n + 1) + for j in range(n + 1 - i) + for k in range(n + 1 - i - j) + ] for p in powers: f = choose(n, p) for a, b in zip(lambdas, p): - f *= a ** b + f *= a**b poly.append(f) return poly @@ -94,8 +94,14 @@ def bernstein_polynomials( class BernsteinFunctional(BaseFunctional): """Functional for a Bernstein element.""" - def __init__(self, reference: Reference, integral_domain: Reference, index: int, - degree: int, entity: typing.Tuple[int, int]): + def __init__( + self, + reference: Reference, + integral_domain: Reference, + index: int, + degree: int, + entity: typing.Tuple[int, int], + ): """Create the functional. Args: @@ -108,15 +114,14 @@ def __init__(self, reference: Reference, integral_domain: Reference, index: int, super().__init__(reference, entity, "identity") orth = [ o / sympy.sqrt((o * o).integral(integral_domain)) - for o in orthogonal_basis(integral_domain.name, degree, 0, t[:integral_domain.tdim])[0] + for o in orthogonal_basis(integral_domain.name, degree, 0, t[: integral_domain.tdim])[0] ] self.ref = integral_domain self.index = index self.degree = degree bern = bernstein_polynomials(degree, integral_domain.tdim, t) - mat = sympy.Matrix( - [[(o * b).integral(integral_domain) for b in bern] for o in orth]) + mat = sympy.Matrix([[(o * b).integral(integral_domain) for b in bern] for o in orth]) minv = mat.inv() alpha = minv.row(index) @@ -157,7 +162,7 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: return f"v\\mapsto c_{{{self.index}}}", [ "\\(v=\\sum_ic_iB_i\\)", f"\\(B_1\\) to \\(B_n\\) " - f"are the degree {self.degree} Bernstein polynomials on the cell" + f"are the degree {self.degree} Bernstein polynomials on the cell", ] else: e = self.entity_tex() @@ -165,7 +170,7 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: f"\\(v=\\sum_ic^{{{e}}}_iB^{{{e}}}_i\\)", f"\\(B^{{{e}}}_1\\) to \\(B^{{{e}}}_n\\) " f"are the degree {self.degree} Bernstein polynomials on \\({e}\\)", - self.entity_definition() + self.entity_definition(), ] @@ -184,13 +189,13 @@ def __init__(self, reference: Reference, order: int): dofs: ListOfFunctionals = [] if order == 0: - dofs = [ - PointEvaluation(reference, reference.midpoint(), (reference.tdim, 0))] + dofs = [PointEvaluation(reference, reference.midpoint(), (reference.tdim, 0))] else: + def index(x: int, y: int = 0, z: int = 0) -> int: """Compute the 1D index.""" return ( - z * (z ** 2 - 3 * z * order - 6 * z + 3 * order ** 2 + 12 * order + 11) // 6 + z * (z**2 - 3 * z * order - 6 * z + 3 * order**2 + 12 * order + 11) // 6 + y * (2 * (order - z) + 3 - y) // 2 + x ) @@ -200,21 +205,30 @@ def index(x: int, y: int = 0, z: int = 0) -> int: for en, _ in enumerate(reference.edges): for i in range(1, order): - dofs.append(BernsteinFunctional( - reference, reference.sub_entity(1, en), i, order, (1, en))) + dofs.append( + BernsteinFunctional( + reference, reference.sub_entity(1, en), i, order, (1, en) + ) + ) for fn, _ in enumerate(reference.faces): for i in range(1, order): for j in range(1, order - i): - dofs.append(BernsteinFunctional( - reference, reference.sub_entity(2, fn), index(i, j), order, (2, fn))) + dofs.append( + BernsteinFunctional( + reference, reference.sub_entity(2, fn), index(i, j), order, (2, fn) + ) + ) if reference.name == "tetrahedon": for i in range(1, order): for j in range(1, order - i): for k in range(1, order - i - j): - dofs.append(BernsteinFunctional( - reference, reference, index(i, j, k), order, (3, 0))) + dofs.append( + BernsteinFunctional( + reference, reference, index(i, j, k), order, (3, 0) + ) + ) super().__init__(reference, order, poly, dofs, reference.tdim, 1) diff --git a/symfem/elements/bfs.py b/symfem/elements/bfs.py index 4e7a954f..1768790a 100644 --- a/symfem/elements/bfs.py +++ b/symfem/elements/bfs.py @@ -28,13 +28,21 @@ def __init__(self, reference: Reference, order: int): for v_n, vs in enumerate(reference.vertices): dofs.append(PointEvaluation(reference, vs, entity=(0, v_n))) for i in range(reference.tdim): - dofs.append(DerivativePointEvaluation( - reference, vs, tuple(1 if i == j else 0 for j in range(reference.tdim)), - entity=(0, v_n))) + dofs.append( + DerivativePointEvaluation( + reference, + vs, + tuple(1 if i == j else 0 for j in range(reference.tdim)), + entity=(0, v_n), + ) + ) if reference.tdim == 2: - dofs.append(DerivativePointEvaluation(reference, vs, (1, 1), entity=(0, v_n), - mapping="identity")) + dofs.append( + DerivativePointEvaluation( + reference, vs, (1, 1), entity=(0, v_n), mapping="identity" + ) + ) poly: typing.List[FunctionInput] = [] poly += quolynomial_set_1d(reference.tdim, order) diff --git a/symfem/elements/bubble.py b/symfem/elements/bubble.py index 49d46c05..178cee72 100644 --- a/symfem/elements/bubble.py +++ b/symfem/elements/bubble.py @@ -43,16 +43,22 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" else: raise ValueError(f"Unsupported reference: {reference.name}") - pn = create_element(reference.name, "Lagrange", o, vertices=reference.vertices, - variant=variant) + pn = create_element( + reference.name, "Lagrange", o, vertices=reference.vertices, variant=variant + ) poly = [bubble * p for p in pn.get_basis_functions()] dofs: ListOfFunctionals = [] if reference.name in ["interval", "triangle", "tetrahedron"]: - def func(i): return sum(i) + + def func(i): + return sum(i) else: - def func(i): return max(i) + + def func(i): + return max(i) + for i in product(range(1, order), repeat=reference.tdim): if func(i) < order: point = reference.get_point(tuple(sympy.Rational(j, order) for j in i[::-1])) @@ -60,9 +66,7 @@ def func(i): return max(i) self.variant = variant - super().__init__( - reference, order, poly, dofs, reference.tdim, 1 - ) + super().__init__(reference, order, poly, dofs, reference.tdim, 1) def init_kwargs(self) -> typing.Dict[str, typing.Any]: """Return the kwargs used to create this element. @@ -74,8 +78,13 @@ def init_kwargs(self) -> typing.Dict[str, typing.Any]: names = ["bubble"] references = ["interval", "triangle", "tetrahedron", "quadrilateral", "hexahedron"] - min_order = {"interval": 2, "triangle": 3, "tetrahedron": 4, - "quadrilateral": 2, "hexahedron": 2} + min_order = { + "interval": 2, + "triangle": 3, + "tetrahedron": 4, + "quadrilateral": 2, + "hexahedron": 2, + } continuity = "C0" last_updated = "2023.09" @@ -101,10 +110,7 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" self.variant = variant - super().__init__( - reference, order, poly, - lagrange.dofs + bubble.dofs, reference.tdim, 1 - ) + super().__init__(reference, order, poly, lagrange.dofs + bubble.dofs, reference.tdim, 1) def init_kwargs(self) -> typing.Dict[str, typing.Any]: """Return the kwargs used to create this element. @@ -141,9 +147,11 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" poly.append((p, 0)) poly.append((0, p)) - dofs: ListOfFunctionals = [DotPointEvaluation(reference, d.dof_point(), v, entity=d.entity) - for d in lagrange.dofs + bubble.dofs - for v in [(1, 0), (0, 1)]] + dofs: ListOfFunctionals = [ + DotPointEvaluation(reference, d.dof_point(), v, entity=d.entity) + for d in lagrange.dofs + bubble.dofs + for v in [(1, 0), (0, 1)] + ] self.variant = variant diff --git a/symfem/elements/conforming_crouzeix_raviart.py b/symfem/elements/conforming_crouzeix_raviart.py index 77bcac50..27369661 100644 --- a/symfem/elements/conforming_crouzeix_raviart.py +++ b/symfem/elements/conforming_crouzeix_raviart.py @@ -33,10 +33,7 @@ def __init__(self, reference: Reference, order: int): poly: typing.List[FunctionInput] = [] poly += polynomial_set_1d(reference.tdim, order) - poly += [ - x[0] ** i * x[1] ** (order - i) * (x[0] + x[1]) - for i in range(1, order) - ] + poly += [x[0] ** i * x[1] ** (order - i) * (x[0] + x[1]) for i in range(1, order)] dofs: ListOfFunctionals = [] for i, v in enumerate(reference.vertices): @@ -44,14 +41,16 @@ def __init__(self, reference: Reference, order: int): if order >= 2: for i, edge in enumerate(reference.edges): for p in range(1, order): - v = tuple(sympy.Rational((order - p) * a + p * b, order) for a, b in zip( - reference.vertices[edge[0]], reference.vertices[edge[1]])) + v = tuple( + sympy.Rational((order - p) * a + p * b, order) + for a, b in zip(reference.vertices[edge[0]], reference.vertices[edge[1]]) + ) dofs.append(PointEvaluation(reference, v, entity=(1, i))) for i in range(1, order): for j in range(1, order + 1 - i): point = ( sympy.Rational(3 * i - 1, 3 * order), - sympy.Rational(3 * j - 1, 3 * order) + sympy.Rational(3 * j - 1, 3 * order), ) dofs.append(PointEvaluation(reference, point, entity=(2, 0))) diff --git a/symfem/elements/crouzeix_raviart.py b/symfem/elements/crouzeix_raviart.py index 9ef3d848..bd71fb9a 100644 --- a/symfem/elements/crouzeix_raviart.py +++ b/symfem/elements/crouzeix_raviart.py @@ -42,10 +42,13 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" dofs.append( PointEvaluation( reference, - tuple(o + sum(a[j] * points[b] - for a, b in zip(entity.axes, i)) - for j, o in enumerate(entity.origin)), - entity=(reference.tdim - 1, e_n))) + tuple( + o + sum(a[j] * points[b] for a, b in zip(entity.axes, i)) + for j, o in enumerate(entity.origin) + ), + entity=(reference.tdim - 1, e_n), + ) + ) points, _ = get_quadrature(variant, order + reference.tdim - 1) for i in product(range(1, order), repeat=reference.tdim): @@ -53,10 +56,13 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" dofs.append( PointEvaluation( reference, - tuple(o + sum(a[j] * points[b] - for a, b in zip(reference.axes, i)) - for j, o in enumerate(reference.origin)), - entity=(reference.tdim, 0))) + tuple( + o + sum(a[j] * points[b] for a, b in zip(reference.axes, i)) + for j, o in enumerate(reference.origin) + ), + entity=(reference.tdim, 0), + ) + ) poly: typing.List[FunctionInput] = [] poly += polynomial_set_1d(reference.tdim, order) diff --git a/symfem/elements/direct_serendipity.py b/symfem/elements/direct_serendipity.py index 230056b3..cca2b364 100644 --- a/symfem/elements/direct_serendipity.py +++ b/symfem/elements/direct_serendipity.py @@ -50,19 +50,19 @@ def __init__(self, reference: Reference, order: int): lambda_12 = xi_v * (1 - x[0]) + eta_v * x[0] for j in range(order - 1): - basis_functions.append((1 - x[1]) * x[1] * lambda_h ** j) + basis_functions.append((1 - x[1]) * x[1] * lambda_h**j) basis_entities.append((1, 1)) for j in range(order - 2): - basis_functions.append((1 - x[1]) * x[1] * lambda_12 * lambda_h ** j) + basis_functions.append((1 - x[1]) * x[1] * lambda_12 * lambda_h**j) basis_entities.append((1, 2)) basis_functions.append((1 - x[1]) * x[1] * r_v * lambda_h ** (order - 2)) basis_entities.append((1, 2)) for j in range(order - 1): - basis_functions.append((1 - x[0]) * x[0] * lambda_v ** j) + basis_functions.append((1 - x[0]) * x[0] * lambda_v**j) basis_entities.append((1, 0)) for j in range(order - 2): - basis_functions.append((1 - x[0]) * x[0] * lambda_34 * lambda_v ** j) + basis_functions.append((1 - x[0]) * x[0] * lambda_34 * lambda_v**j) basis_entities.append((1, 3)) basis_functions.append((1 - x[0]) * x[0] * r_h * lambda_v ** (order - 2)) basis_entities.append((1, 3)) @@ -73,8 +73,7 @@ def __init__(self, reference: Reference, order: int): basis_functions.append(f * x[0] * x[1] * (1 - x[0]) * (1 - x[1])) basis_entities.append((2, 0)) - super().__init__(reference, order, basis_functions, basis_entities, - reference.tdim, 1) + super().__init__(reference, order, basis_functions, basis_entities, reference.tdim, 1) names = ["direct serendipity"] references = ["quadrilateral"] diff --git a/symfem/elements/dpc.py b/symfem/elements/dpc.py index d9eab80c..6ecd45a0 100644 --- a/symfem/elements/dpc.py +++ b/symfem/elements/dpc.py @@ -30,12 +30,16 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" if isinstance(d, PointEvaluation): dofs.append(PointEvaluation(reference, d.point, entity=(reference.tdim, 0))) elif isinstance(d, IntegralAgainst): - dofs.append(IntegralAgainst( - reference, d.f * reference.jacobian(), entity=(reference.tdim, 0))) + dofs.append( + IntegralAgainst( + reference, d.f * reference.jacobian(), entity=(reference.tdim, 0) + ) + ) else: if order == 0: - points = [reference.get_point(tuple( - sympy.Rational(1, 2) for _ in range(reference.tdim)))] + points = [ + reference.get_point(tuple(sympy.Rational(1, 2) for _ in range(reference.tdim))) + ] else: points = [ reference.get_point(tuple(sympy.Rational(j, order) for j in i[::-1])) @@ -43,16 +47,13 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" if sum(i) <= order ] - dofs = [ - PointEvaluation(reference, d, entity=(reference.tdim, 0)) for d in points] + dofs = [PointEvaluation(reference, d, entity=(reference.tdim, 0)) for d in points] poly: typing.List[FunctionInput] = [] poly += polynomial_set_1d(reference.tdim, order) poly = reference.map_polyset_from_default(poly) - super().__init__( - reference, order, poly, dofs, reference.tdim, 1 - ) + super().__init__(reference, order, poly, dofs, reference.tdim, 1) self.variant = variant def init_kwargs(self) -> typing.Dict[str, typing.Any]: @@ -87,7 +88,7 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" scalar_space = DPC(reference, order, variant) dofs: ListOfFunctionals = [] if reference.tdim == 1: - directions: typing.List[typing.Tuple[int, ...]] = [(1, )] + directions: typing.List[typing.Tuple[int, ...]] = [(1,)] else: directions = [ tuple(1 if i == j else 0 for j in range(reference.tdim)) diff --git a/symfem/elements/dual.py b/symfem/elements/dual.py index eea16088..5e677dc2 100644 --- a/symfem/elements/dual.py +++ b/symfem/elements/dual.py @@ -19,13 +19,18 @@ class DualCiarletElement(FiniteElement): """Abstract barycentric finite element.""" def __init__( - self, dual_coefficients: typing.List[typing.List[typing.List[ - typing.Union[int, sympy.core.expr.Expr]]]], - fine_space: str, reference: DualPolygon, order: int, + self, + dual_coefficients: typing.List[ + typing.List[typing.List[typing.Union[int, sympy.core.expr.Expr]]] + ], + fine_space: str, + reference: DualPolygon, + order: int, dof_entities: typing.List[typing.Tuple[int, int]], - domain_dim: int, range_dim: int, + domain_dim: int, + range_dim: int, range_shape: typing.Optional[typing.Tuple[int, ...]] = None, - dof_directions: typing.Optional[SetOfPoints] = None + dof_directions: typing.Optional[SetOfPoints] = None, ): """Create a dual element. @@ -46,15 +51,14 @@ def __init__( self.dual_coefficients = dual_coefficients self.fine_space = fine_space - super().__init__(reference, order, len(dual_coefficients), domain_dim, range_dim, - range_shape=range_shape) + super().__init__( + reference, order, len(dual_coefficients), domain_dim, range_dim, range_shape=range_shape + ) self._basis_functions: typing.Union[typing.List[AnyFunction], None] = None self._dof_entities = dof_entities self._dof_directions = dof_directions - def get_polynomial_basis( - self, reshape: bool = True - ) -> typing.List[AnyFunction]: + def get_polynomial_basis(self, reshape: bool = True) -> typing.List[AnyFunction]: """Get the symbolic polynomial basis for the element. Returns: @@ -97,8 +101,9 @@ def get_basis_functions( v0 = self.reference.origin pieces: typing.Dict[SetOfPointsInput, FunctionInput] = {} for coeffs, v1, v2 in zip( - coeff_list, self.reference.vertices, - self.reference.vertices[1:] + self.reference.vertices[:1] + coeff_list, + self.reference.vertices, + self.reference.vertices[1:] + self.reference.vertices[:1], ): sub_basis = sub_e.map_to_cell((v0, v1, v2)) @@ -150,10 +155,15 @@ def dof_plot_positions(self) -> typing.List[PointType]: if dim == 0: positions.append(self.reference.vertices[e_n]) elif dim == 1: - positions.append(tuple((a + b) / 2 for a, b in zip( - self.reference.vertices[self.reference.edges[e_n][0]], - self.reference.vertices[self.reference.edges[e_n][1]], - ))) + positions.append( + tuple( + (a + b) / 2 + for a, b in zip( + self.reference.vertices[self.reference.edges[e_n][0]], + self.reference.vertices[self.reference.edges[e_n][1]], + ) + ) + ) elif dim == 2: positions.append(self.reference.midpoint()) else: @@ -180,10 +190,11 @@ def dof_entities(self) -> typing.List[typing.Tuple[int, int]]: return self._dof_entities def map_to_cell( - self, vertices_in: SetOfPointsInput, basis: - typing.Optional[typing.List[AnyFunction]] = None, + self, + vertices_in: SetOfPointsInput, + basis: typing.Optional[typing.List[AnyFunction]] = None, forward_map: typing.Optional[PointType] = None, - inverse_map: typing.Optional[PointType] = None + inverse_map: typing.Optional[PointType] = None, ) -> typing.List[AnyFunction]: """Map the basis onto a cell using the appropriate mapping for the element. @@ -209,18 +220,19 @@ def __init__(self, reference: DualPolygon, order: int): reference: The reference element order: The polynomial order """ - dual_coefficients: typing.List[typing.List[typing.List[ - typing.Union[int, sympy.core.expr.Expr]]]] = [] + dual_coefficients: typing.List[ + typing.List[typing.List[typing.Union[int, sympy.core.expr.Expr]]] + ] = [] if order == 0: - dual_coefficients = [ - [[1] for i in range(2 * reference.number_of_triangles)] - ] + dual_coefficients = [[[1] for i in range(2 * reference.number_of_triangles)]] fine_space = "Lagrange" dof_entities = [(2, 0)] else: dual_coefficients = [ - [[sympy.Rational(1, reference.number_of_triangles), 0, 0] - for i in range(2 * reference.number_of_triangles)] + [ + [sympy.Rational(1, reference.number_of_triangles), 0, 0] + for i in range(2 * reference.number_of_triangles) + ] for j in range(reference.number_of_triangles) ] @@ -259,10 +271,10 @@ def __init__(self, reference: DualPolygon, order: int): order: The polynomial order """ assert order == 1 - dual_coefficients: typing.List[typing.List[typing.List[ - typing.Union[int, sympy.core.expr.Expr]]]] = [ - [[0, 0, 0] - for i in range(2 * reference.number_of_triangles)] + dual_coefficients: typing.List[ + typing.List[typing.List[typing.Union[int, sympy.core.expr.Expr]]] + ] = [ + [[0, 0, 0] for i in range(2 * reference.number_of_triangles)] for j in range(reference.number_of_triangles) ] @@ -277,12 +289,20 @@ def __init__(self, reference: DualPolygon, order: int): dof_entities = [(0, i) for i in range(0, len(reference.vertices), 2)] dof_directions: typing.List[PointType] = [] for i in range(0, len(reference.vertices), 2): - dof_directions.append(tuple( - b - a for a, b in zip(reference.origin, reference.vertices[i]))) + dof_directions.append( + tuple(b - a for a, b in zip(reference.origin, reference.vertices[i])) + ) super().__init__( - dual_coefficients, "RT", reference, order, dof_entities, reference.tdim, 2, - dof_directions=tuple(dof_directions)) + dual_coefficients, + "RT", + reference, + order, + dof_entities, + reference.tdim, + 2, + dof_directions=tuple(dof_directions), + ) names = ["Buffa-Christiansen", "BC"] references = ["dual polygon"] @@ -303,10 +323,10 @@ def __init__(self, reference: DualPolygon, order: int): order: The polynomial order """ assert order == 1 - dual_coefficients: typing.List[typing.List[typing.List[ - typing.Union[int, sympy.core.expr.Expr]]]] = [ - [[0, 0, 0] - for i in range(2 * reference.number_of_triangles)] + dual_coefficients: typing.List[ + typing.List[typing.List[typing.Union[int, sympy.core.expr.Expr]]] + ] = [ + [[0, 0, 0] for i in range(2 * reference.number_of_triangles)] for j in range(reference.number_of_triangles) ] @@ -321,12 +341,20 @@ def __init__(self, reference: DualPolygon, order: int): dof_entities = [(0, i) for i in range(0, len(reference.vertices), 2)] dof_directions: typing.List[PointType] = [] for i in range(0, len(reference.vertices), 2): - dof_directions.append(tuple( - b - a for a, b in zip(reference.origin, reference.vertices[i]))) + dof_directions.append( + tuple(b - a for a, b in zip(reference.origin, reference.vertices[i])) + ) super().__init__( - dual_coefficients, "N1curl", reference, order, dof_entities, - reference.tdim, 2, dof_directions=tuple(dof_directions)) + dual_coefficients, + "N1curl", + reference, + order, + dof_entities, + reference.tdim, + 2, + dof_directions=tuple(dof_directions), + ) names = ["rotated Buffa-Christiansen", "RBC"] references = ["dual polygon"] diff --git a/symfem/elements/fortin_soulie.py b/symfem/elements/fortin_soulie.py index 144c4330..59fc384e 100644 --- a/symfem/elements/fortin_soulie.py +++ b/symfem/elements/fortin_soulie.py @@ -38,7 +38,7 @@ def __init__(self, reference: Reference, order: int): PointEvaluation(reference, (0, third), entity=(1, 1)), PointEvaluation(reference, (0, two_thirds), entity=(1, 1)), PointEvaluation(reference, (sympy.Rational(1, 2), 0), entity=(1, 2)), - PointEvaluation(reference, (third, third), entity=(2, 0)) + PointEvaluation(reference, (third, third), entity=(2, 0)), ] poly: typing.List[FunctionInput] = [] diff --git a/symfem/elements/guzman_neilan.py b/symfem/elements/guzman_neilan.py index a380e840..cab8f9f6 100644 --- a/symfem/elements/guzman_neilan.py +++ b/symfem/elements/guzman_neilan.py @@ -42,10 +42,15 @@ def __init__(self, reference: Reference, order: int): for n in range(reference.sub_entity_count(reference.tdim - 1)): facet = reference.sub_entity(reference.tdim - 1, n) for v in facet.vertices: - dofs.append(DotPointEvaluation( - reference, v, tuple(i * facet.jacobian() for i in facet.normal()), - entity=(reference.tdim - 1, n), - mapping="contravariant")) + dofs.append( + DotPointEvaluation( + reference, + v, + tuple(i * facet.jacobian() for i in facet.normal()), + entity=(reference.tdim - 1, n), + mapping="contravariant", + ) + ) if order == 2: assert reference.name == "tetrahedron" @@ -53,26 +58,38 @@ def __init__(self, reference: Reference, order: int): # Midpoints of edges for n in range(reference.sub_entity_count(1)): edge = reference.sub_entity(1, n) - dofs.append(DotPointEvaluation( - reference, edge.midpoint(), tuple(i * edge.jacobian() for i in edge.tangent()), - entity=(1, n), mapping="contravariant")) + dofs.append( + DotPointEvaluation( + reference, + edge.midpoint(), + tuple(i * edge.jacobian() for i in edge.tangent()), + entity=(1, n), + mapping="contravariant", + ) + ) # Midpoints of edges of faces for n in range(reference.sub_entity_count(2)): face = reference.sub_entity(2, n) for m in range(3): edge = face.sub_entity(1, m) - dofs.append(DotPointEvaluation( - reference, edge.midpoint(), - tuple(i * face.jacobian() for i in face.normal()), - entity=(2, n), mapping="contravariant")) + dofs.append( + DotPointEvaluation( + reference, + edge.midpoint(), + tuple(i * face.jacobian() for i in face.normal()), + entity=(2, n), + mapping="contravariant", + ) + ) # Interior edges for v in reference.vertices: p = tuple((i + sympy.Rational(1, 4)) / 2 for i in v) for d in [(1, 0, 0), (0, 1, 0), (0, 0, 1)]: - dofs.append(DotPointEvaluation( - reference, p, d, entity=(3, 0), mapping="contravariant")) + dofs.append( + DotPointEvaluation(reference, p, d, entity=(3, 0), mapping="contravariant") + ) dofs += make_integral_moment_dofs( reference, @@ -82,8 +99,11 @@ def __init__(self, reference: Reference, order: int): mid = reference.midpoint() for i in range(reference.tdim): direction = tuple(1 if i == j else 0 for j in range(reference.tdim)) - dofs.append(DotPointEvaluation(reference, mid, direction, entity=(reference.tdim, 0), - mapping="contravariant")) + dofs.append( + DotPointEvaluation( + reference, mid, direction, entity=(reference.tdim, 0), mapping="contravariant" + ) + ) super().__init__(reference, order, poly, dofs, reference.tdim, reference.tdim) @@ -108,7 +128,8 @@ def _make_polyset_triangle( sub_tris: typing.List[SetOfPoints] = [ (reference.vertices[0], reference.vertices[1], mid), (reference.vertices[0], reference.vertices[2], mid), - (reference.vertices[1], reference.vertices[2], mid)] + (reference.vertices[1], reference.vertices[2], mid), + ] basis: typing.List[FunctionInput] = [] basis += make_piecewise_lagrange(sub_tris, "triangle", order) @@ -152,7 +173,8 @@ def _make_polyset_tetrahedron( (reference.vertices[0], reference.vertices[1], reference.vertices[2], mid), (reference.vertices[0], reference.vertices[1], reference.vertices[3], mid), (reference.vertices[0], reference.vertices[2], reference.vertices[3], mid), - (reference.vertices[1], reference.vertices[2], reference.vertices[3], mid)] + (reference.vertices[1], reference.vertices[2], reference.vertices[3], mid), + ] basis: typing.List[FunctionInput] = [] basis += make_piecewise_lagrange(sub_tets, "tetrahedron", order) @@ -184,8 +206,11 @@ def _make_polyset_tetrahedron( def make_piecewise_lagrange( - sub_cells: typing.List[SetOfPoints], cell_name, order: int, zero_on_boundary: bool = False, - zero_at_centre: bool = False + sub_cells: typing.List[SetOfPoints], + cell_name, + order: int, + zero_on_boundary: bool = False, + zero_at_centre: bool = False, ) -> typing.List[PiecewiseFunction]: """Make the basis functions of a piecewise Lagrange space. @@ -200,6 +225,7 @@ def make_piecewise_lagrange( The basis functions """ from symfem import create_reference + lagrange_space = VectorLagrange(create_reference(cell_name), order) lagrange_bases: typing.List[typing.List[VectorFunction]] = [] for c in sub_cells: @@ -211,17 +237,17 @@ def make_piecewise_lagrange( lagrange_bases.append(row) basis_dofs: typing.List[typing.Tuple[int, ...]] = [] - zero: typing.Tuple[int, ...] = (0, ) + zero: typing.Tuple[int, ...] = (0,) if cell_name == "triangle": cell_tdim = 2 - for dim, tri_entities in enumerate([ - [(0, 0, -1), (1, -1, 0), (-1, 1, 1), (2, 2, 2)], - [(0, -1, 1), (1, 1, -1), (-1, 0, 0), - (2, -1, -1), (-1, 2, -1), (-1, -1, 2)], - [(0, -1, -1), (-1, 0, -1), (-1, -1, 0)] - ]): - nones = [ - -1 for i in lagrange_space.entity_dofs(dim, 0)] + for dim, tri_entities in enumerate( + [ + [(0, 0, -1), (1, -1, 0), (-1, 1, 1), (2, 2, 2)], + [(0, -1, 1), (1, 1, -1), (-1, 0, 0), (2, -1, -1), (-1, 2, -1), (-1, -1, 2)], + [(0, -1, -1), (-1, 0, -1), (-1, -1, 0)], + ] + ): + nones = [-1 for i in lagrange_space.entity_dofs(dim, 0)] for tri_e in tri_entities: if dim == 0: if zero_on_boundary and (0 in tri_e or 1 in tri_e): @@ -231,26 +257,44 @@ def make_piecewise_lagrange( elif dim == 1: if zero_on_boundary and (2 in tri_e): continue - doflist = [ - nones if i == -1 else lagrange_space.entity_dofs(dim, i) for i in tri_e - ] + doflist = [nones if i == -1 else lagrange_space.entity_dofs(dim, i) for i in tri_e] for dofs in zip(*doflist): basis_dofs.append(dofs) zero = (0, 0) elif cell_name == "tetrahedron": cell_tdim = 3 - for dim, tet_entities in enumerate([ - [(0, 0, 0, -1), (1, 1, -1, 0), (2, -1, 1, 1), (-1, 2, 2, 2), (3, 3, 3, 3)], - [(2, -1, -1, 5), (5, 5, -1, -1), (4, -1, 5, -1), (-1, 2, -1, 4), - (-1, 4, 4, -1), (-1, -1, 2, 2), (3, 3, 3, -1), (-1, 0, 0, 0), (0, -1, 1, 1), - (1, 1, -1, 3)], - [(0, -1, -1, 2), (1, -1, 2, -1), (2, 2, -1, -1), (3, -1, -1, -1), (-1, 0, -1, 1), - (-1, 1, 1, -1), (-1, 3, -1, -1), (-1, -1, 0, 0), (-1, -1, 3, -1), (-1, -1, -1, 3)], - [(0, -1, -1, -1), (-1, 0, -1, -1), (-1, -1, 0, -1), (-1, -1, -1, 0)] - ]): - nones = [ - -1 for i in lagrange_space.entity_dofs(dim, 0)] + for dim, tet_entities in enumerate( + [ + [(0, 0, 0, -1), (1, 1, -1, 0), (2, -1, 1, 1), (-1, 2, 2, 2), (3, 3, 3, 3)], + [ + (2, -1, -1, 5), + (5, 5, -1, -1), + (4, -1, 5, -1), + (-1, 2, -1, 4), + (-1, 4, 4, -1), + (-1, -1, 2, 2), + (3, 3, 3, -1), + (-1, 0, 0, 0), + (0, -1, 1, 1), + (1, 1, -1, 3), + ], + [ + (0, -1, -1, 2), + (1, -1, 2, -1), + (2, 2, -1, -1), + (3, -1, -1, -1), + (-1, 0, -1, 1), + (-1, 1, 1, -1), + (-1, 3, -1, -1), + (-1, -1, 0, 0), + (-1, -1, 3, -1), + (-1, -1, -1, 3), + ], + [(0, -1, -1, -1), (-1, 0, -1, -1), (-1, -1, 0, -1), (-1, -1, -1, 0)], + ] + ): + nones = [-1 for i in lagrange_space.entity_dofs(dim, 0)] for tet_e in tet_entities: if dim == 0: if zero_on_boundary and (0 in tet_e or 1 in tet_e or 2 in tet_e): @@ -263,9 +307,7 @@ def make_piecewise_lagrange( elif dim == 2: if zero_on_boundary and (3 in tet_e): continue - doflist = [ - nones if i == -1 else lagrange_space.entity_dofs(dim, i) for i in tet_e - ] + doflist = [nones if i == -1 else lagrange_space.entity_dofs(dim, i) for i in tet_e] for dofs in zip(*doflist): basis_dofs.append(dofs) zero = (0, 0, 0) diff --git a/symfem/elements/hct.py b/symfem/elements/hct.py index d57b7f78..9fc8f742 100644 --- a/symfem/elements/hct.py +++ b/symfem/elements/hct.py @@ -9,8 +9,12 @@ import sympy from ..finite_element import CiarletElement -from ..functionals import (DerivativePointEvaluation, ListOfFunctionals, PointEvaluation, - PointNormalDerivativeEvaluation) +from ..functionals import ( + DerivativePointEvaluation, + ListOfFunctionals, + PointEvaluation, + PointNormalDerivativeEvaluation, +) from ..functions import FunctionInput, ScalarFunction from ..piecewise_functions import PiecewiseFunction from ..references import NonDefaultReferenceError, Reference @@ -39,40 +43,87 @@ def __init__(self, reference: Reference, order: int): dofs.append(DerivativePointEvaluation(reference, vs, (0, 1), entity=(0, v_n))) for e_n in range(reference.sub_entity_count(1)): sub_ref = reference.sub_entity(1, e_n) - dofs.append(PointNormalDerivativeEvaluation( - reference, sub_ref.midpoint(), sub_ref, entity=(1, e_n))) + dofs.append( + PointNormalDerivativeEvaluation( + reference, sub_ref.midpoint(), sub_ref, entity=(1, e_n) + ) + ) mid = tuple(sympy.Rational(sum(i), len(i)) for i in zip(*reference.vertices)) subs = [ (reference.vertices[0], reference.vertices[1], mid), (reference.vertices[1], reference.vertices[2], mid), - (reference.vertices[2], reference.vertices[0], mid)] + (reference.vertices[2], reference.vertices[0], mid), + ] - piece_list = [tuple(ScalarFunction(p) for _ in range(3)) - for p in [1, x[0], x[1], x[0]**2, x[0]*x[1], x[1]**2, - x[0]**3, x[0]**2*x[1], x[0]*x[1]**2, x[1]**3]] - piece_list.append(( - ScalarFunction(-23*x[0]**3 + 24*x[0]**2*x[1] - 12*x[0]*x[1]**2 + 36*x[1]**2), - ScalarFunction( - -28*x[0]**3 + 12*x[0]**2*x[1] + 9*x[0]**2 - 3*x[0] + 32*x[1]**3 + 12*x[1] - 1), - ScalarFunction(-15*x[0]**2 - 33*x[0]*x[1]**2 + 30*x[0]*x[1] + 22*x[1]**3 + 21*x[1]**2))) - piece_list.append(( - ScalarFunction( - 22*x[0]**3 - 21*x[0]**2*x[1] - 12*x[0]*x[1]**2 + 30*x[0]*x[1] - 24*x[1]**2), - ScalarFunction( - 32*x[0]**3 + 12*x[0]**2*x[1] - 21*x[0]**2 + 12*x[0] - 28*x[1]**3 - 3*x[1] - 1), - ScalarFunction(15*x[0]**2 + 12*x[0]*x[1]**2 - 23*x[1]**3 - 9*x[1]**2))) + piece_list = [ + tuple(ScalarFunction(p) for _ in range(3)) + for p in [ + 1, + x[0], + x[1], + x[0] ** 2, + x[0] * x[1], + x[1] ** 2, + x[0] ** 3, + x[0] ** 2 * x[1], + x[0] * x[1] ** 2, + x[1] ** 3, + ] + ] + piece_list.append( + ( + ScalarFunction( + -23 * x[0] ** 3 + 24 * x[0] ** 2 * x[1] - 12 * x[0] * x[1] ** 2 + 36 * x[1] ** 2 + ), + ScalarFunction( + -28 * x[0] ** 3 + + 12 * x[0] ** 2 * x[1] + + 9 * x[0] ** 2 + - 3 * x[0] + + 32 * x[1] ** 3 + + 12 * x[1] + - 1 + ), + ScalarFunction( + -15 * x[0] ** 2 + - 33 * x[0] * x[1] ** 2 + + 30 * x[0] * x[1] + + 22 * x[1] ** 3 + + 21 * x[1] ** 2 + ), + ) + ) + piece_list.append( + ( + ScalarFunction( + 22 * x[0] ** 3 + - 21 * x[0] ** 2 * x[1] + - 12 * x[0] * x[1] ** 2 + + 30 * x[0] * x[1] + - 24 * x[1] ** 2 + ), + ScalarFunction( + 32 * x[0] ** 3 + + 12 * x[0] ** 2 * x[1] + - 21 * x[0] ** 2 + + 12 * x[0] + - 28 * x[1] ** 3 + - 3 * x[1] + - 1 + ), + ScalarFunction( + 15 * x[0] ** 2 + 12 * x[0] * x[1] ** 2 - 23 * x[1] ** 3 - 9 * x[1] ** 2 + ), + ) + ) poly: typing.List[FunctionInput] = [] - poly += [ - PiecewiseFunction({i: j for i, j in zip(subs, p)}, 2) - for p in piece_list] + poly += [PiecewiseFunction({i: j for i, j in zip(subs, p)}, 2) for p in piece_list] poly = reference.map_polyset_from_default(poly) - super().__init__( - reference, order, poly, dofs, reference.tdim, 1 - ) + super().__init__(reference, order, poly, dofs, reference.tdim, 1) names = ["Hsieh-Clough-Tocher", "Clough-Tocher", "HCT", "CT"] references = ["triangle"] diff --git a/symfem/elements/hermite.py b/symfem/elements/hermite.py index 8cffe117..5485bb9b 100644 --- a/symfem/elements/hermite.py +++ b/symfem/elements/hermite.py @@ -28,9 +28,14 @@ def __init__(self, reference: Reference, order: int): for v_n, v in enumerate(reference.vertices): dofs.append(PointEvaluation(reference, v, entity=(0, v_n))) for i in range(reference.tdim): - dofs.append(DerivativePointEvaluation( - reference, v, tuple(1 if i == j else 0 for j in range(reference.tdim)), - entity=(0, v_n))) + dofs.append( + DerivativePointEvaluation( + reference, + v, + tuple(1 if i == j else 0 for j in range(reference.tdim)), + entity=(0, v_n), + ) + ) for e_n in range(reference.sub_entity_count(2)): sub_entity = reference.sub_entity(2, e_n) dofs.append(PointEvaluation(reference, sub_entity.midpoint(), entity=(2, e_n))) diff --git a/symfem/elements/hhj.py b/symfem/elements/hhj.py index 3a2d2d3c..fdcb15cf 100644 --- a/symfem/elements/hhj.py +++ b/symfem/elements/hhj.py @@ -43,26 +43,31 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" directions_extra: typing.List[typing.Tuple[typing.Tuple[int, ...], ...]] = [] if reference.tdim == 2: - poly = [((p[0], p[1]), (p[1], p[2])) - for p in polynomial_set_vector(reference.tdim, 3, order)] - directions = [((0, 1), (1, 0)), - ((-2, 1), (1, 0)), - ((0, -1), (-1, 2))] + poly = [ + ((p[0], p[1]), (p[1], p[2])) + for p in polynomial_set_vector(reference.tdim, 3, order) + ] + directions = [((0, 1), (1, 0)), ((-2, 1), (1, 0)), ((0, -1), (-1, 2))] directions_extra = [] if reference.tdim == 3: - poly = [((p[0], p[1], p[2]), (p[1], p[3], p[4]), (p[2], p[4], p[5])) - for p in polynomial_set_vector(reference.tdim, 6, order)] - directions = [((0, 1, 1), (1, 0, 1), (1, 1, 0)), - ((-6, 1, 1), (1, 0, 1), (1, 1, 0)), - ((0, 1, 1), (1, -6, 1), (1, 1, 0)), - ((0, 1, 1), (1, 0, 1), (1, 1, -6))] - directions_extra = [((0, 0, -1), (0, 0, 1), (-1, 1, 0)), - ((0, -1, 0), (-1, 0, 1), (0, 1, 0))] + poly = [ + ((p[0], p[1], p[2]), (p[1], p[3], p[4]), (p[2], p[4], p[5])) + for p in polynomial_set_vector(reference.tdim, 6, order) + ] + directions = [ + ((0, 1, 1), (1, 0, 1), (1, 1, 0)), + ((-6, 1, 1), (1, 0, 1), (1, 1, 0)), + ((0, 1, 1), (1, -6, 1), (1, 1, 0)), + ((0, 1, 1), (1, 0, 1), (1, 1, -6)), + ] + directions_extra = [ + ((0, 0, -1), (0, 0, 1), (-1, 1, 0)), + ((0, -1, 0), (-1, 0, 1), (0, 1, 0)), + ] dofs: ListOfFunctionals = make_integral_moment_dofs( reference, - facets=(NormalInnerProductIntegralMoment, Lagrange, order, - {"variant": variant}), + facets=(NormalInnerProductIntegralMoment, Lagrange, order, {"variant": variant}), ) # cell functions @@ -71,22 +76,41 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" basis = space.get_basis_functions() for p, dof in zip(basis, space.dofs): for d in directions: - dofs.append(IntegralMoment( - reference, p * _MatrixFunction(d), dof, entity=(reference.tdim, 0), - mapping="double_contravariant")) + dofs.append( + IntegralMoment( + reference, + p * _MatrixFunction(d), + dof, + entity=(reference.tdim, 0), + mapping="double_contravariant", + ) + ) # cell functions extra space_extra = Lagrange(reference, order, variant) basis_extra = space_extra.get_basis_functions() for p, dof in zip(basis_extra, space_extra.dofs): for d in directions_extra: - dofs.append(IntegralMoment( - reference, p * _MatrixFunction(d), dof, entity=(reference.tdim, 0), - mapping="double_contravariant")) + dofs.append( + IntegralMoment( + reference, + p * _MatrixFunction(d), + dof, + entity=(reference.tdim, 0), + mapping="double_contravariant", + ) + ) self.variant = variant - super().__init__(reference, order, poly, dofs, reference.tdim, reference.tdim ** 2, - (reference.tdim, reference.tdim)) + super().__init__( + reference, + order, + poly, + dofs, + reference.tdim, + reference.tdim**2, + (reference.tdim, reference.tdim), + ) def init_kwargs(self) -> typing.Dict[str, typing.Any]: """Return the kwargs used to create this element. diff --git a/symfem/elements/huang_zhang.py b/symfem/elements/huang_zhang.py index bdacb070..d6465f86 100644 --- a/symfem/elements/huang_zhang.py +++ b/symfem/elements/huang_zhang.py @@ -7,8 +7,12 @@ import typing from ..finite_element import CiarletElement -from ..functionals import (IntegralAgainst, ListOfFunctionals, NormalIntegralMoment, - TangentIntegralMoment) +from ..functionals import ( + IntegralAgainst, + ListOfFunctionals, + NormalIntegralMoment, + TangentIntegralMoment, +) from ..functions import FunctionInput, VectorFunction from ..moments import make_integral_moment_dofs from ..references import NonDefaultReferenceError, Reference @@ -48,21 +52,17 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" dofs += make_integral_moment_dofs( reference, - facets=(NormalIntegralMoment, Lagrange, order - 1, - {"variant": variant}), + facets=(NormalIntegralMoment, Lagrange, order - 1, {"variant": variant}), ) dofs += make_integral_moment_dofs( reference, - facets=(TangentIntegralMoment, Lagrange, order - 2, - {"variant": variant}), + facets=(TangentIntegralMoment, Lagrange, order - 2, {"variant": variant}), ) for i in range(order - 1): for j in range(order - 2): - dofs.append(IntegralAgainst( - reference, (x[0] ** i * x[1] ** j, 0), (2, 0))) - dofs.append(IntegralAgainst( - reference, (0, x[0] ** j * x[1] ** i), (2, 0))) + dofs.append(IntegralAgainst(reference, (x[0] ** i * x[1] ** j, 0), (2, 0))) + dofs.append(IntegralAgainst(reference, (0, x[0] ** j * x[1] ** i), (2, 0))) super().__init__(reference, order, poly, dofs, reference.tdim, reference.tdim) diff --git a/symfem/elements/kmv.py b/symfem/elements/kmv.py index 2f5ec2cd..ec6995d9 100644 --- a/symfem/elements/kmv.py +++ b/symfem/elements/kmv.py @@ -30,8 +30,7 @@ def kmv_tri_polyset(m: int, mf: int) -> typing.List[FunctionInput]: poly += polynomial_set_1d(2, m) b = x[0] * x[1] * (1 - x[0] - x[1]) - poly += [x[0] ** p * x[1] ** (mf - 3 - p) * b - for p in range(mf - 2)] + poly += [x[0] ** p * x[1] ** (mf - 3 - p) * b for p in range(mf - 2)] return poly @@ -82,46 +81,85 @@ def __init__(self, reference: Reference, order: int): if reference.name == "triangle": if order == 1: for v_n, v in enumerate(reference.vertices): - dofs.append(WeightedPointEvaluation( - reference, v, sympy.Rational(1, 6), entity=(0, v_n))) + dofs.append( + WeightedPointEvaluation(reference, v, sympy.Rational(1, 6), entity=(0, v_n)) + ) poly = kmv_tri_polyset(1, 1) elif order == 3: for v_n, v in enumerate(reference.vertices): - dofs.append(WeightedPointEvaluation( - reference, v, sympy.Rational(1, 40), entity=(0, v_n))) + dofs.append( + WeightedPointEvaluation( + reference, v, sympy.Rational(1, 40), entity=(0, v_n) + ) + ) for e_n, e in enumerate(reference.edges): - midpoint = tuple(sympy.Rational(i + j, 2) for i, j in zip( - reference.vertices[e[0]], reference.vertices[e[1]])) - dofs.append(WeightedPointEvaluation( - reference, midpoint, sympy.Rational(1, 15), entity=(1, e_n))) - dofs.append(WeightedPointEvaluation( - reference, (sympy.Rational(1, 3), sympy.Rational(1, 3)), sympy.Rational(9, 40), - entity=(2, 0))) + midpoint = tuple( + sympy.Rational(i + j, 2) + for i, j in zip(reference.vertices[e[0]], reference.vertices[e[1]]) + ) + dofs.append( + WeightedPointEvaluation( + reference, midpoint, sympy.Rational(1, 15), entity=(1, e_n) + ) + ) + dofs.append( + WeightedPointEvaluation( + reference, + (sympy.Rational(1, 3), sympy.Rational(1, 3)), + sympy.Rational(9, 40), + entity=(2, 0), + ) + ) poly = kmv_tri_polyset(2, 3) elif order == 4: for v_n, v in enumerate(reference.vertices): - dofs.append(WeightedPointEvaluation( - reference, v, sympy.Rational(1, 90) - sympy.sqrt(7) / 720, - entity=(0, v_n))) + dofs.append( + WeightedPointEvaluation( + reference, + v, + sympy.Rational(1, 90) - sympy.sqrt(7) / 720, + entity=(0, v_n), + ) + ) alpha = sympy.Rational(1, 2) - sympy.sqrt(411 - 84 * (7 - sympy.sqrt(7))) / 42 for e_n, e in enumerate(reference.edges): - dofs.append(WeightedPointEvaluation( - reference, tuple(i + (j - i) * alpha for i, j in zip( - reference.vertices[e[0]], reference.vertices[e[1]])), - sympy.Rational(7, 720) - sympy.sqrt(7) / 180, entity=(1, e_n))) - dofs.append(WeightedPointEvaluation( - reference, tuple(j + (i - j) * alpha for i, j in zip( - reference.vertices[e[0]], reference.vertices[e[1]])), - sympy.Rational(7, 720) - sympy.sqrt(7) / 180, entity=(1, e_n))) + dofs.append( + WeightedPointEvaluation( + reference, + tuple( + i + (j - i) * alpha + for i, j in zip(reference.vertices[e[0]], reference.vertices[e[1]]) + ), + sympy.Rational(7, 720) - sympy.sqrt(7) / 180, + entity=(1, e_n), + ) + ) + dofs.append( + WeightedPointEvaluation( + reference, + tuple( + j + (i - j) * alpha + for i, j in zip(reference.vertices[e[0]], reference.vertices[e[1]]) + ), + sympy.Rational(7, 720) - sympy.sqrt(7) / 180, + entity=(1, e_n), + ) + ) beta = (1 - 1 / sympy.sqrt(7)) / 3 for i in [(0, 1, 2), (1, 2, 0), (2, 0, 1)]: - dofs.append(WeightedPointEvaluation( - reference, tuple(a + beta * (b - a) + beta * (c - a) - for a, b, c in zip(*[reference.vertices[j] for j in i])), - sympy.Rational(49, 360) - 7 * sympy.sqrt(7) / 720, - entity=(2, 0))) + dofs.append( + WeightedPointEvaluation( + reference, + tuple( + a + beta * (b - a) + beta * (c - a) + for a, b, c in zip(*[reference.vertices[j] for j in i]) + ), + sympy.Rational(49, 360) - 7 * sympy.sqrt(7) / 720, + entity=(2, 0), + ) + ) poly = kmv_tri_polyset(3, 4) else: @@ -130,32 +168,53 @@ def __init__(self, reference: Reference, order: int): elif reference.name == "tetrahedron": if order == 1: for v_n, v in enumerate(reference.vertices): - dofs.append(WeightedPointEvaluation( - reference, v, sympy.Rational(1, 24), entity=(0, v_n))) + dofs.append( + WeightedPointEvaluation( + reference, v, sympy.Rational(1, 24), entity=(0, v_n) + ) + ) poly = kmv_tet_polyset(1, 1, 1) elif order == 4: for v_n, v in enumerate(reference.vertices): - dofs.append(WeightedPointEvaluation( - reference, v, (13 - 3 * sympy.sqrt(13)) / 10080, entity=(0, v_n))) + dofs.append( + WeightedPointEvaluation( + reference, v, (13 - 3 * sympy.sqrt(13)) / 10080, entity=(0, v_n) + ) + ) for e_n, e in enumerate(reference.edges): - midpoint = tuple(sympy.Rational(i + j, 2) for i, j in zip( - reference.vertices[e[0]], reference.vertices[e[1]])) - dofs.append(WeightedPointEvaluation( - reference, midpoint, (4 - sympy.sqrt(13)) / 315, entity=(1, e_n))) + midpoint = tuple( + sympy.Rational(i + j, 2) + for i, j in zip(reference.vertices[e[0]], reference.vertices[e[1]]) + ) + dofs.append( + WeightedPointEvaluation( + reference, midpoint, (4 - sympy.sqrt(13)) / 315, entity=(1, e_n) + ) + ) alpha = (7 - sympy.sqrt(13)) / 18 for f_n, face in enumerate(reference.faces): for i in [(0, 1, 2), (1, 2, 0), (2, 0, 1)]: - dofs.append(WeightedPointEvaluation( - reference, tuple( - a + alpha * (b - a) + alpha * (c - a) - for a, b, c in zip(*[reference.vertices[face[j]] for j in i])), - (29 + 17 * sympy.sqrt(13)) / 10080, - entity=(2, f_n))) - - dofs.append(WeightedPointEvaluation( - reference, tuple(sympy.Rational(1, 4) for i in range(3)), - sympy.Rational(16, 315), entity=(3, 0))) + dofs.append( + WeightedPointEvaluation( + reference, + tuple( + a + alpha * (b - a) + alpha * (c - a) + for a, b, c in zip(*[reference.vertices[face[j]] for j in i]) + ), + (29 + 17 * sympy.sqrt(13)) / 10080, + entity=(2, f_n), + ) + ) + + dofs.append( + WeightedPointEvaluation( + reference, + tuple(sympy.Rational(1, 4) for i in range(3)), + sympy.Rational(16, 315), + entity=(3, 0), + ) + ) poly = kmv_tet_polyset(2, 4, 4) else: @@ -163,9 +222,7 @@ def __init__(self, reference: Reference, order: int): else: raise NotImplementedError - super().__init__( - reference, order, poly, dofs, reference.tdim, 1 - ) + super().__init__(reference, order, poly, dofs, reference.tdim, 1) names = ["Kong-Mulder-Veldhuizen", "KMV"] references = ["triangle", "tetrahedron"] diff --git a/symfem/elements/lagrange.py b/symfem/elements/lagrange.py index e9c02c4f..28dca27d 100644 --- a/symfem/elements/lagrange.py +++ b/symfem/elements/lagrange.py @@ -8,8 +8,12 @@ from ..finite_element import CiarletElement from ..functionals import DotPointEvaluation, IntegralAgainst, ListOfFunctionals, PointEvaluation from ..functions import FunctionInput -from ..polynomials import (lobatto_dual_basis, orthonormal_basis, polynomial_set_1d, - polynomial_set_vector) +from ..polynomials import ( + lobatto_dual_basis, + orthonormal_basis, + polynomial_set_1d, + polynomial_set_vector, +) from ..quadrature import get_quadrature from ..references import Reference @@ -33,11 +37,11 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" elif order == 0: dofs.append( PointEvaluation( - reference, reference.get_point(tuple( - sympy.Rational(1, reference.tdim + 1) - for i in range(reference.tdim) - )), - entity=(reference.tdim, 0) + reference, + reference.get_point( + tuple(sympy.Rational(1, reference.tdim + 1) for i in range(reference.tdim)) + ), + entity=(reference.tdim, 0), ) ) elif variant == "lobatto": @@ -62,8 +66,7 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" for i in product(range(1, order), repeat=edim): if sum(i) < order: point = entity.get_point([points[j] for j in i[::-1]]) - dofs.append(PointEvaluation(reference, point, - entity=(edim, e_n))) + dofs.append(PointEvaluation(reference, point, entity=(edim, e_n))) poly: typing.List[FunctionInput] = [] poly += polynomial_set_1d(reference.tdim, order) @@ -104,8 +107,9 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" if isinstance(p, PointEvaluation): dofs.append(PointEvaluation(reference, p.dof_point(), entity=p.entity)) elif isinstance(p, IntegralAgainst): - dofs.append(IntegralAgainst( - reference, p.f * reference.jacobian(), entity=p.entity)) + dofs.append( + IntegralAgainst(reference, p.f * reference.jacobian(), entity=p.entity) + ) poly += polynomial_set_1d(reference.tdim, order) else: @@ -116,11 +120,13 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" for p in scalar_space.dofs: for d in directions: if isinstance(p, PointEvaluation): - dofs.append(DotPointEvaluation( - reference, p.dof_point(), d, entity=p.entity)) + dofs.append( + DotPointEvaluation(reference, p.dof_point(), d, entity=p.entity) + ) elif isinstance(p, IntegralAgainst): - dofs.append(IntegralAgainst( - reference, tuple(p.f * i for i in d), entity=p.entity)) + dofs.append( + IntegralAgainst(reference, tuple(p.f * i for i in d), entity=p.entity) + ) poly += polynomial_set_vector(reference.tdim, reference.tdim, order) @@ -156,21 +162,28 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" scalar_space = Lagrange(reference, order, variant) dofs: ListOfFunctionals = [] if reference.tdim == 1: - directions: typing.List[typing.Tuple[int, ...]] = [(1, )] + directions: typing.List[typing.Tuple[int, ...]] = [(1,)] else: directions = [ - tuple(1 if i == j else 0 for j in range(reference.tdim ** 2)) - for i in range(reference.tdim ** 2) + tuple(1 if i == j else 0 for j in range(reference.tdim**2)) + for i in range(reference.tdim**2) ] for p in scalar_space.dofs: for d in directions: dofs.append(DotPointEvaluation(reference, p.dof_point(), d, entity=p.entity)) poly: typing.List[FunctionInput] = [] - poly += polynomial_set_vector(reference.tdim, reference.tdim ** 2, order) - - super().__init__(reference, order, poly, dofs, reference.tdim, reference.tdim ** 2, - (reference.tdim, reference.tdim)) + poly += polynomial_set_vector(reference.tdim, reference.tdim**2, order) + + super().__init__( + reference, + order, + poly, + dofs, + reference.tdim, + reference.tdim**2, + (reference.tdim, reference.tdim), + ) self.variant = variant def init_kwargs(self) -> typing.Dict[str, typing.Any]: @@ -207,34 +220,45 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" for p in scalar_space.dofs: dofs.append(PointEvaluation(reference, p.dof_point(), entity=p.entity)) super().__init__( - reference, order, poly, dofs, - reference.tdim, reference.tdim ** 2, + reference, + order, + poly, + dofs, + reference.tdim, + reference.tdim**2, ) else: directions: typing.List[typing.Tuple[typing.Tuple[int, ...], ...]] = [] if reference.tdim == 2: poly += [((a[0], a[1]), (a[1], a[2])) for a in polynomial_set_vector(2, 3, order)] - directions = [((1, 0), (0, 0)), ((0, 1), (0, 0)), - ((0, 0), (0, 1))] + directions = [((1, 0), (0, 0)), ((0, 1), (0, 0)), ((0, 0), (0, 1))] else: assert reference.tdim == 3 - poly += [((a[0], a[1], a[2]), - (a[1], a[3], a[4]), - (a[2], a[4], a[5])) for a in polynomial_set_vector(3, 6, order)] - directions = [((1, 0, 0), (0, 0, 0), (0, 0, 0)), - ((0, 1, 0), (0, 0, 0), (0, 0, 0)), - ((0, 0, 1), (0, 0, 0), (0, 0, 0)), - ((0, 0, 0), (0, 1, 0), (0, 0, 0)), - ((0, 0, 0), (0, 0, 1), (0, 0, 0)), - ((0, 0, 0), (0, 0, 0), (0, 0, 1))] + poly += [ + ((a[0], a[1], a[2]), (a[1], a[3], a[4]), (a[2], a[4], a[5])) + for a in polynomial_set_vector(3, 6, order) + ] + directions = [ + ((1, 0, 0), (0, 0, 0), (0, 0, 0)), + ((0, 1, 0), (0, 0, 0), (0, 0, 0)), + ((0, 0, 1), (0, 0, 0), (0, 0, 0)), + ((0, 0, 0), (0, 1, 0), (0, 0, 0)), + ((0, 0, 0), (0, 0, 1), (0, 0, 0)), + ((0, 0, 0), (0, 0, 0), (0, 0, 1)), + ] for p in scalar_space.dofs: for d in directions: dofs.append(DotPointEvaluation(reference, p.dof_point(), d, entity=p.entity)) super().__init__( - reference, order, poly, dofs, - reference.tdim, reference.tdim ** 2, (reference.tdim, reference.tdim), + reference, + order, + poly, + dofs, + reference.tdim, + reference.tdim**2, + (reference.tdim, reference.tdim), ) self.variant = variant diff --git a/symfem/elements/lagrange_prism.py b/symfem/elements/lagrange_prism.py index b1bc6c78..8c7a1452 100644 --- a/symfem/elements/lagrange_prism.py +++ b/symfem/elements/lagrange_prism.py @@ -35,11 +35,9 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" elif order == 0: dofs = [ PointEvaluation( - reference, tuple( - sympy.Rational(1, reference.tdim + 1) - for i in range(reference.tdim) - ), - entity=(reference.tdim, 0) + reference, + tuple(sympy.Rational(1, reference.tdim + 1) for i in range(reference.tdim)), + entity=(reference.tdim, 0), ) ] elif variant == "lobatto": @@ -56,9 +54,14 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" for i in range(1, order): dofs.append( PointEvaluation( - reference, tuple(o + entity.axes[0][j] * points[i] - for j, o in enumerate(entity.origin)), - entity=(1, e_n))) + reference, + tuple( + o + entity.axes[0][j] * points[i] + for j, o in enumerate(entity.origin) + ), + entity=(1, e_n), + ) + ) # Faces for e_n in range(reference.sub_entity_count(2)): entity = reference.sub_entity(2, e_n) @@ -66,18 +69,28 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" if len(entity.vertices) == 4 or sum(ii) < order: dofs.append( PointEvaluation( - reference, tuple( + reference, + tuple( o + sum(a[j] * points[b] for a, b in zip(entity.axes, ii[::-1])) - for j, o in enumerate(entity.origin)), entity=(2, e_n))) + for j, o in enumerate(entity.origin) + ), + entity=(2, e_n), + ) + ) # Interior for ii in product(range(1, order), repeat=3): if ii[0] + ii[1] < order: dofs.append( PointEvaluation( - reference, tuple( + reference, + tuple( o + sum(a[j] * points[b] for a, b in zip(reference.axes, ii)) - for j, o in enumerate(reference.origin)), entity=(3, 0))) + for j, o in enumerate(reference.origin) + ), + entity=(3, 0), + ) + ) poly: typing.List[FunctionInput] = [] poly += prism_polynomial_set_1d(reference.tdim, order) diff --git a/symfem/elements/lagrange_pyramid.py b/symfem/elements/lagrange_pyramid.py index bb85b5c6..d200be9a 100644 --- a/symfem/elements/lagrange_pyramid.py +++ b/symfem/elements/lagrange_pyramid.py @@ -39,11 +39,9 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" elif order == 0: dofs = [ PointEvaluation( - reference, tuple( - sympy.Rational(1, reference.tdim + 1) - for i in range(reference.tdim) - ), - entity=(reference.tdim, 0) + reference, + tuple(sympy.Rational(1, reference.tdim + 1) for i in range(reference.tdim)), + entity=(reference.tdim, 0), ) ] elif variant == "lobatto": @@ -60,9 +58,14 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" for i in range(1, order): dofs.append( PointEvaluation( - reference, tuple(o + entity.axes[0][j] * points[i] - for j, o in enumerate(entity.origin)), - entity=(1, e_n))) + reference, + tuple( + o + entity.axes[0][j] * points[i] + for j, o in enumerate(entity.origin) + ), + entity=(1, e_n), + ) + ) # Faces for e_n in range(reference.sub_entity_count(2)): entity = reference.sub_entity(2, e_n) @@ -70,20 +73,28 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" if len(entity.vertices) == 4 or sum(ii) < order: dofs.append( PointEvaluation( - reference, tuple(o + sum(a[j] * points[b] - for a, b in zip(entity.axes, ii[::-1])) - for j, o in enumerate(entity.origin)), - entity=(2, e_n))) + reference, + tuple( + o + sum(a[j] * points[b] for a, b in zip(entity.axes, ii[::-1])) + for j, o in enumerate(entity.origin) + ), + entity=(2, e_n), + ) + ) # Interior for ii in product(range(1, order), repeat=3): if max(ii[0], ii[1]) + ii[2] < order: dofs.append( PointEvaluation( - reference, tuple(o + sum(a[j] * points[b] - for a, b in zip(reference.axes, ii)) - for j, o in enumerate(reference.origin)), - entity=(3, 0))) + reference, + tuple( + o + sum(a[j] * points[b] for a, b in zip(reference.axes, ii)) + for j, o in enumerate(reference.origin) + ), + entity=(3, 0), + ) + ) poly: typing.List[FunctionInput] = [] poly += pyramid_polynomial_set_1d(reference.tdim, order) diff --git a/symfem/elements/morley.py b/symfem/elements/morley.py index 1d2c20f2..784099ed 100644 --- a/symfem/elements/morley.py +++ b/symfem/elements/morley.py @@ -34,7 +34,8 @@ def __init__(self, reference: Reference, order: int): sub_ref = reference.sub_entity(1, e_n) midpoint = sub_ref.midpoint() dofs.append( - PointNormalDerivativeEvaluation(reference, midpoint, sub_ref, entity=(1, e_n))) + PointNormalDerivativeEvaluation(reference, midpoint, sub_ref, entity=(1, e_n)) + ) poly: typing.List[FunctionInput] = [] poly += polynomial_set_1d(reference.tdim, order) diff --git a/symfem/elements/morley_wang_xu.py b/symfem/elements/morley_wang_xu.py index 6113819b..da972315 100644 --- a/symfem/elements/morley_wang_xu.py +++ b/symfem/elements/morley_wang_xu.py @@ -7,8 +7,12 @@ import typing from ..finite_element import CiarletElement -from ..functionals import (IntegralAgainst, IntegralOfDirectionalMultiderivative, ListOfFunctionals, - PointEvaluation) +from ..functionals import ( + IntegralAgainst, + IntegralOfDirectionalMultiderivative, + ListOfFunctionals, + PointEvaluation, +) from ..functions import FunctionInput from ..polynomials import polynomial_set_1d from ..references import NonDefaultReferenceError, Reference @@ -41,8 +45,9 @@ def __init__(self, reference: Reference, order: int): dim = reference.tdim - 1 for facet_n in range(reference.sub_entity_count(dim)): facet = reference.sub_entity(dim, facet_n) - dofs.append(IntegralAgainst(reference, 1 / facet.jacobian(), - entity=(dim, facet_n))) + dofs.append( + IntegralAgainst(reference, 1 / facet.jacobian(), entity=(dim, facet_n)) + ) elif order == 2: if reference.tdim == 2: for v_n, v in enumerate(reference.vertices): @@ -51,14 +56,21 @@ def __init__(self, reference: Reference, order: int): dim = reference.tdim - 2 for ridge_n in range(reference.sub_entity_count(dim)): ridge = reference.sub_entity(dim, ridge_n) - dofs.append(IntegralAgainst(reference, 1 / ridge.jacobian(), - entity=(dim, ridge_n))) + dofs.append( + IntegralAgainst(reference, 1 / ridge.jacobian(), entity=(dim, ridge_n)) + ) dim = reference.tdim - 1 for facet_n in range(reference.sub_entity_count(dim)): facet = reference.sub_entity(dim, facet_n) - dofs.append(IntegralOfDirectionalMultiderivative( - reference, (facet.normal(), ), (1, ), (dim, facet_n), - scale=1 / facet.jacobian())) + dofs.append( + IntegralOfDirectionalMultiderivative( + reference, + (facet.normal(),), + (1,), + (dim, facet_n), + scale=1 / facet.jacobian(), + ) + ) else: assert order == reference.tdim == 3 for v_n, v in enumerate(reference.vertices): @@ -72,15 +84,19 @@ def __init__(self, reference: Reference, order: int): face = reference.sub_entity(2, f_n) normals.append(face.normal()) for orders in [(1, 0), (0, 1)]: - dofs.append(IntegralOfDirectionalMultiderivative( - reference, tuple(normals), orders, (1, e_n), - scale=1 / volume)) + dofs.append( + IntegralOfDirectionalMultiderivative( + reference, tuple(normals), orders, (1, e_n), scale=1 / volume + ) + ) for f_n, vs in enumerate(reference.sub_entities(2)): subentity = reference.sub_entity(2, f_n) volume = subentity.jacobian() - dofs.append(IntegralOfDirectionalMultiderivative( - reference, (subentity.normal(), ), (2, ), (2, f_n), - scale=1 / volume)) + dofs.append( + IntegralOfDirectionalMultiderivative( + reference, (subentity.normal(),), (2,), (2, f_n), scale=1 / volume + ) + ) super().__init__(reference, order, poly, dofs, reference.tdim, 1) diff --git a/symfem/elements/mtw.py b/symfem/elements/mtw.py index 8516c268..3ef807dc 100644 --- a/symfem/elements/mtw.py +++ b/symfem/elements/mtw.py @@ -8,8 +8,12 @@ import typing from ..finite_element import CiarletElement -from ..functionals import (IntegralMoment, ListOfFunctionals, NormalIntegralMoment, - TangentIntegralMoment) +from ..functionals import ( + IntegralMoment, + ListOfFunctionals, + NormalIntegralMoment, + TangentIntegralMoment, +) from ..functions import FunctionInput, VectorFunction from ..moments import make_integral_moment_dofs from ..polynomials import polynomial_set_vector @@ -35,37 +39,51 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" raise NonDefaultReferenceError() dofs: ListOfFunctionals = make_integral_moment_dofs( - reference, facets=(NormalIntegralMoment, Lagrange, 1, - "contravariant", {"variant": variant})) + reference, + facets=(NormalIntegralMoment, Lagrange, 1, "contravariant", {"variant": variant}), + ) poly: typing.List[FunctionInput] = [] if reference.name == "triangle": - poly += [(1, 0), (x[0], 0), (x[1], 0), - (0, 1), (0, x[0]), (0, x[1]), - # (x**2 + 2*x*y, -2*x*y - y**2) - (x[0] ** 2 + 2 * x[0] * x[1], - -2 * x[0] * x[1] - x[1] ** 2), - # (-x**3 + 2*x**2 + 3*x*y**2, 3*x**2*y - 4*x*y - y**3) - (-x[0] ** 3 + 2 * x[0] ** 2 + 3 * x[0] * x[1] ** 2, - 3 * x[0] ** 2 * x[1] - 4 * x[0] * x[1] - x[1] ** 3), - # (2*x**2*y + x**2 + 3*x*y**2, -2*x*y**2 - 2*x*y - y**3) - (2 * x[0] ** 2 * x[1] + x[0] ** 2 + 3 * x[0] * x[1] ** 2, - -2 * x[0] * x[1] ** 2 - 2 * x[0] * x[1] - x[1] ** 3)] + poly += [ + (1, 0), + (x[0], 0), + (x[1], 0), + (0, 1), + (0, x[0]), + (0, x[1]), + # (x**2 + 2*x*y, -2*x*y - y**2) + (x[0] ** 2 + 2 * x[0] * x[1], -2 * x[0] * x[1] - x[1] ** 2), + # (-x**3 + 2*x**2 + 3*x*y**2, 3*x**2*y - 4*x*y - y**3) + ( + -(x[0] ** 3) + 2 * x[0] ** 2 + 3 * x[0] * x[1] ** 2, + 3 * x[0] ** 2 * x[1] - 4 * x[0] * x[1] - x[1] ** 3, + ), + # (2*x**2*y + x**2 + 3*x*y**2, -2*x*y**2 - 2*x*y - y**3) + ( + 2 * x[0] ** 2 * x[1] + x[0] ** 2 + 3 * x[0] * x[1] ** 2, + -2 * x[0] * x[1] ** 2 - 2 * x[0] * x[1] - x[1] ** 3, + ), + ] dofs += make_integral_moment_dofs( - reference, facets=(TangentIntegralMoment, Lagrange, 0, - "contravariant", {"variant": variant})) + reference, + facets=(TangentIntegralMoment, Lagrange, 0, "contravariant", {"variant": variant}), + ) else: assert reference.name == "tetrahedron" poly += polynomial_set_vector(reference.tdim, reference.tdim, 1) for p in polynomial_set_vector(reference.tdim, reference.tdim, 1): - poly.append(VectorFunction(tuple( - i * x[0] * x[1] * x[2] * (1 - x[0] - x[1] - x[2]) - for i in p)).curl()) + poly.append( + VectorFunction( + tuple(i * x[0] * x[1] * x[2] * (1 - x[0] - x[1] - x[2]) for i in p) + ).curl() + ) dofs += make_integral_moment_dofs( - reference, facets=(IntegralMoment, NedelecFirstKind, 1, "contravariant", - {"variant": variant})) + reference, + facets=(IntegralMoment, NedelecFirstKind, 1, "contravariant", {"variant": variant}), + ) super().__init__(reference, order, poly, dofs, reference.tdim, reference.tdim) self.variant = variant diff --git a/symfem/elements/nedelec.py b/symfem/elements/nedelec.py index c7a43a49..d328c3b1 100644 --- a/symfem/elements/nedelec.py +++ b/symfem/elements/nedelec.py @@ -32,12 +32,9 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" poly += Hcurl_polynomials(reference.tdim, reference.tdim, order) dofs: ListOfFunctionals = make_integral_moment_dofs( reference, - edges=(TangentIntegralMoment, Lagrange, order - 1, - {"variant": variant}), - faces=(IntegralMoment, VectorLagrange, order - 2, "covariant", - {"variant": variant}), - volumes=(IntegralMoment, VectorLagrange, order - 3, "covariant", - {"variant": variant}), + edges=(TangentIntegralMoment, Lagrange, order - 1, {"variant": variant}), + faces=(IntegralMoment, VectorLagrange, order - 2, "covariant", {"variant": variant}), + volumes=(IntegralMoment, VectorLagrange, order - 3, "covariant", {"variant": variant}), ) super().__init__(reference, order, poly, dofs, reference.tdim, reference.tdim) diff --git a/symfem/elements/nedelec_prism.py b/symfem/elements/nedelec_prism.py index 434e8b9c..b6ee6aa4 100644 --- a/symfem/elements/nedelec_prism.py +++ b/symfem/elements/nedelec_prism.py @@ -33,19 +33,33 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" poly += [ (i[0] * j, i[1] * j, 0) for i in polynomial_set_vector(2, 2, order - 1) + Hcurl_polynomials(2, 2, order) - for j in polynomial_set_1d(1, order, x[2:])] - poly += [(0, 0, i * j) - for i in polynomial_set_1d(2, order, x[:2]) - for j in polynomial_set_1d(1, order - 1, x[2:])] + for j in polynomial_set_1d(1, order, x[2:]) + ] + poly += [ + (0, 0, i * j) + for i in polynomial_set_1d(2, order, x[:2]) + for j in polynomial_set_1d(1, order - 1, x[2:]) + ] dofs: ListOfFunctionals = make_integral_moment_dofs( reference, - edges=(TangentIntegralMoment, Lagrange, order - 1, - {"variant": variant}), - faces={"triangle": (IntegralMoment, VectorLagrange, order - 2, - "covariant", {"variant": variant}), - "quadrilateral": (IntegralMoment, QRT, order - 1, "covariant", - {"variant": variant})}, + edges=(TangentIntegralMoment, Lagrange, order - 1, {"variant": variant}), + faces={ + "triangle": ( + IntegralMoment, + VectorLagrange, + order - 2, + "covariant", + {"variant": variant}, + ), + "quadrilateral": ( + IntegralMoment, + QRT, + order - 1, + "covariant", + {"variant": variant}, + ), + }, ) triangle = create_reference("triangle") @@ -60,11 +74,12 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" # TODO: correct these for order > 2 for i in range(space1.space_dim): for j in range(space2.space_dim): - f = (space2.get_basis_function(j) * space1.get_basis_function(i)[0], - space2.get_basis_function(j) * space1.get_basis_function(i)[1], - 0) - dofs.append(IntegralAgainst( - reference, f, entity=(3, 0), mapping="covariant")) + f = ( + space2.get_basis_function(j) * space1.get_basis_function(i)[0], + space2.get_basis_function(j) * space1.get_basis_function(i)[1], + 0, + ) + dofs.append(IntegralAgainst(reference, f, entity=(3, 0), mapping="covariant")) super().__init__(reference, order, poly, dofs, reference.tdim, reference.tdim) self.variant = variant diff --git a/symfem/elements/p1_iso_p2.py b/symfem/elements/p1_iso_p2.py index 1ca7194f..6ea94ca1 100644 --- a/symfem/elements/p1_iso_p2.py +++ b/symfem/elements/p1_iso_p2.py @@ -26,9 +26,9 @@ def __init__(self, reference: Reference, order: int): reference: The reference element order: The polynomial order """ - zero = reference.get_point((sympy.Integer(0), )) - half = reference.get_point((sympy.Rational(1, 2), )) - one = reference.get_point((sympy.Integer(1), )) + zero = reference.get_point((sympy.Integer(0),)) + half = reference.get_point((sympy.Rational(1, 2),)) + one = reference.get_point((sympy.Integer(1),)) x = reference.get_inverse_map_to_self()[0] poly: typing.List[FunctionInput] = [ @@ -43,9 +43,7 @@ def __init__(self, reference: Reference, order: int): entity = reference.sub_entity(1, 0) dofs.append(PointEvaluation(reference, entity.midpoint(), entity=(1, 0))) - super().__init__( - reference, order, poly, dofs, reference.tdim, 1 - ) + super().__init__(reference, order, poly, dofs, reference.tdim, 1) names = ["P1-iso-P2", "P2-iso-P1", "iso-P2 P1"] references = ["interval"] @@ -87,12 +85,15 @@ def __init__(self, reference: Reference, order: int): {0: 2 * y, 2: 2 * c, 3: 1 - 2 * x}, {1: 2 * y, 2: 2 * x, 3: 1 - 2 * c}, ]: - poly.append(PiecewiseFunction({ - tuple( - reference.get_point(pt) for pt in q - ): pieces[i] if i in pieces else 0 - for i, q in enumerate(tris) - }, 2)) + poly.append( + PiecewiseFunction( + { + tuple(reference.get_point(pt) for pt in q): pieces[i] if i in pieces else 0 + for i, q in enumerate(tris) + }, + 2, + ) + ) dofs: ListOfFunctionals = [] for v_n, v in enumerate(reference.vertices): @@ -101,9 +102,7 @@ def __init__(self, reference: Reference, order: int): entity = reference.sub_entity(1, e_n) dofs.append(PointEvaluation(reference, entity.midpoint(), entity=(1, e_n))) - super().__init__( - reference, order, poly, dofs, reference.tdim, 1 - ) + super().__init__(reference, order, poly, dofs, reference.tdim, 1) names = ["P1-iso-P2", "P2-iso-P1", "iso-P2 P1"] references = ["triangle"] @@ -147,11 +146,15 @@ def __init__(self, reference: Reference, order: int): {2: 2 * x * (2 * y - 1), 3: 2 * (1 - x) * (2 * y - 1)}, {0: 4 * x * y, 1: 4 * (1 - x) * y, 2: 4 * x * (1 - y), 3: 4 * (1 - x) * (1 - y)}, ]: - poly.append(PiecewiseFunction({ - tuple( - reference.get_point(pt) for pt in q - ): pieces[i] if i in pieces else 0 - for i, q in enumerate(quads)}, 2)) + poly.append( + PiecewiseFunction( + { + tuple(reference.get_point(pt) for pt in q): pieces[i] if i in pieces else 0 + for i, q in enumerate(quads) + }, + 2, + ) + ) dofs: ListOfFunctionals = [] for v_n, v in enumerate(reference.vertices): @@ -161,9 +164,7 @@ def __init__(self, reference: Reference, order: int): dofs.append(PointEvaluation(reference, entity.midpoint(), entity=(1, e_n))) dofs.append(PointEvaluation(reference, reference.midpoint(), entity=(2, 0))) - super().__init__( - reference, order, poly, dofs, reference.tdim, 1 - ) + super().__init__(reference, order, poly, dofs, reference.tdim, 1) names = ["P1-iso-P2", "P2-iso-P1", "iso-P2 P1"] references = ["quadrilateral"] diff --git a/symfem/elements/p1_macro.py b/symfem/elements/p1_macro.py index 1a7862aa..951a1519 100644 --- a/symfem/elements/p1_macro.py +++ b/symfem/elements/p1_macro.py @@ -41,20 +41,22 @@ def __init__(self, reference: Reference, order: int): PiecewiseFunction({q: 1 for q in tris}, 2), PiecewiseFunction({q: x[0] for q in tris}, 2), PiecewiseFunction({q: x[1] for q in tris}, 2), - PiecewiseFunction({ - tris[0]: 3 * invmap[1], - tris[1]: 3 * (1 - invmap[0] - invmap[1]), - tris[2]: 3 * invmap[0], - }, 2)] + PiecewiseFunction( + { + tris[0]: 3 * invmap[1], + tris[1]: 3 * (1 - invmap[0] - invmap[1]), + tris[2]: 3 * invmap[0], + }, + 2, + ), + ] dofs: ListOfFunctionals = [] for v_n, v in enumerate(reference.vertices): dofs.append(PointEvaluation(reference, v, entity=(0, v_n))) dofs.append(IntegralAgainst(reference, 1, entity=(2, 0))) - super().__init__( - reference, order, poly, dofs, reference.tdim, 1 - ) + super().__init__(reference, order, poly, dofs, reference.tdim, 1) names = ["P1 macro"] references = ["triangle"] diff --git a/symfem/elements/q.py b/symfem/elements/q.py index c98dcdd2..d4f62457 100644 --- a/symfem/elements/q.py +++ b/symfem/elements/q.py @@ -6,12 +6,25 @@ import sympy from ..finite_element import CiarletElement, FiniteElement -from ..functionals import (DotPointEvaluation, IntegralAgainst, IntegralMoment, ListOfFunctionals, - NormalIntegralMoment, PointEvaluation, TangentIntegralMoment) +from ..functionals import ( + DotPointEvaluation, + IntegralAgainst, + IntegralMoment, + ListOfFunctionals, + NormalIntegralMoment, + PointEvaluation, + TangentIntegralMoment, +) from ..functions import FunctionInput from ..moments import make_integral_moment_dofs -from ..polynomials import (Hcurl_quolynomials, Hdiv_quolynomials, lobatto_dual_basis, - orthonormal_basis, quolynomial_set_1d, quolynomial_set_vector) +from ..polynomials import ( + Hcurl_quolynomials, + Hdiv_quolynomials, + lobatto_dual_basis, + orthonormal_basis, + quolynomial_set_1d, + quolynomial_set_vector, +) from ..quadrature import get_quadrature from ..references import NonDefaultReferenceError, Reference @@ -33,10 +46,13 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" for f in basis: dofs.append(IntegralAgainst(reference, f, (reference.tdim, 0))) elif order == 0: - dofs = [PointEvaluation( - reference, - reference.get_point(tuple(sympy.Rational(1, 2) for i in range(reference.tdim))), - entity=(reference.tdim, 0))] + dofs = [ + PointEvaluation( + reference, + reference.get_point(tuple(sympy.Rational(1, 2) for i in range(reference.tdim))), + entity=(reference.tdim, 0), + ) + ] elif variant == "lobatto": if reference != reference.default_reference(): raise NonDefaultReferenceError() @@ -59,10 +75,14 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" for i in product(range(1, order), repeat=edim): dofs.append( PointEvaluation( - reference, tuple(o + sum(a[j] * points[b] - for a, b in zip(entity.axes, i[::-1])) - for j, o in enumerate(entity.origin)), - entity=(edim, e_n))) + reference, + tuple( + o + sum(a[j] * points[b] for a, b in zip(entity.axes, i[::-1])) + for j, o in enumerate(entity.origin) + ), + entity=(edim, e_n), + ) + ) poly: typing.List[FunctionInput] = [] poly += quolynomial_set_1d(reference.tdim, order) @@ -72,7 +92,7 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" self.variant = variant def get_tensor_factorisation( - self + self, ) -> typing.List[typing.Tuple[str, typing.List[FiniteElement], typing.List[int]]]: """Get the representation of the element as a tensor product. @@ -82,6 +102,7 @@ def get_tensor_factorisation( if self.variant == "lobatto": return super().get_tensor_factorisation() from symfem import create_element + interval_q = create_element("interval", "Lagrange", self.order) if self.order == 0: @@ -98,20 +119,20 @@ def get_tensor_factorisation( perm += [2, 6] + [8 + 6 * n + i for i in range(n)] for i in range(n): perm += [8 + n + i, 8 + 9 * n + i] - perm += [8 + 12 * n + 2 * n ** 2 + i + n * j for j in range(n)] + perm += [8 + 12 * n + 2 * n**2 + i + n * j for j in range(n)] perm += [1, 5] + [8 + 4 * n + i for i in range(n)] perm += [3, 7] + [8 + 7 * n + i for i in range(n)] for i in range(n): perm += [8 + 3 * n + i, 8 + 10 * n + i] - perm += [8 + 12 * n + 3 * n ** 2 + i + n * j for j in range(n)] + perm += [8 + 12 * n + 3 * n**2 + i + n * j for j in range(n)] for i in range(n): perm += [8 + i, 8 + 8 * n + i] - perm += [8 + 12 * n + n ** 2 + i + n * j for j in range(n)] + perm += [8 + 12 * n + n**2 + i + n * j for j in range(n)] perm += [8 + 5 * n + i, 8 + 11 * n + i] - perm += [8 + 12 * n + 4 * n ** 2 + i + n * j for j in range(n)] + perm += [8 + 12 * n + 4 * n**2 + i + n * j for j in range(n)] for j in range(n): - perm += [8 + 12 * n + i + n * j, 8 + 12 * n + 5 * n ** 2 + i + n * j] - perm += [8 + 12 * n + 6 * n ** 2 + i + n * j + n ** 2 * k for k in range(n)] + perm += [8 + 12 * n + i + n * j, 8 + 12 * n + 5 * n**2 + i + n * j] + perm += [8 + 12 * n + 6 * n**2 + i + n * j + n**2 * k for k in range(n)] return [("scalar", [interval_q for i in range(self.reference.tdim)], perm)] diff --git a/symfem/elements/rannacher_turek.py b/symfem/elements/rannacher_turek.py index 2e6ba8b6..91539df2 100644 --- a/symfem/elements/rannacher_turek.py +++ b/symfem/elements/rannacher_turek.py @@ -34,9 +34,9 @@ def __init__(self, reference: Reference, order: int): poly: typing.List[FunctionInput] = [] if reference.name == "quadrilateral": - poly += [1, x[0], x[1], x[0]**2 - x[1]**2] + poly += [1, x[0], x[1], x[0] ** 2 - x[1] ** 2] else: - poly += [1, x[0], x[1], x[2], x[0]**2 - x[1]**2, x[1]**2 - x[2]**2] + poly += [1, x[0], x[1], x[2], x[0] ** 2 - x[1] ** 2, x[1] ** 2 - x[2] ** 2] super().__init__(reference, order, poly, dofs, reference.tdim, 1) diff --git a/symfem/elements/regge.py b/symfem/elements/regge.py index 4d750dcb..86090b2c 100644 --- a/symfem/elements/regge.py +++ b/symfem/elements/regge.py @@ -13,8 +13,13 @@ import sympy from ..finite_element import CiarletElement -from ..functionals import (InnerProductIntegralMoment, IntegralAgainst, IntegralMoment, - ListOfFunctionals, PointInnerProduct) +from ..functionals import ( + InnerProductIntegralMoment, + IntegralAgainst, + IntegralMoment, + ListOfFunctionals, + PointInnerProduct, +) from ..functions import FunctionInput from ..moments import make_integral_moment_dofs from ..polynomials import polynomial_set_vector @@ -35,13 +40,18 @@ def __init__(self, reference: Reference, order: int, variant: str = "point"): variant: The variant of the element """ from symfem import create_reference + poly: typing.List[FunctionInput] = [] if reference.tdim == 2: - poly = [((p[0], p[1]), (p[1], p[2])) - for p in polynomial_set_vector(reference.tdim, 3, order)] + poly = [ + ((p[0], p[1]), (p[1], p[2])) + for p in polynomial_set_vector(reference.tdim, 3, order) + ] if reference.tdim == 3: - poly = [((p[0], p[1], p[3]), (p[1], p[2], p[4]), (p[3], p[4], p[5])) - for p in polynomial_set_vector(reference.tdim, 6, order)] + poly = [ + ((p[0], p[1], p[3]), (p[1], p[2], p[4]), (p[3], p[4], p[5])) + for p in polynomial_set_vector(reference.tdim, 6, order) + ] dofs: ListOfFunctionals = [] if variant == "point": @@ -53,33 +63,63 @@ def __init__(self, reference: Reference, order: int, variant: str = "point"): for i in product(range(1, order + 2), repeat=edim): if sum(i) < order + 2: for edge in entity.edges[::-1]: - tangent = tuple(b - a for a, b in zip( - entity.vertices[edge[0]], entity.vertices[edge[1]])) - dofs.append(PointInnerProduct( - reference, tuple(o + sum(sympy.Rational(a[j] * b, order + 2) - for a, b in zip(entity.axes, i[::-1])) - for j, o in enumerate(entity.origin)), - tangent, tangent, entity=(edim, e_n), - mapping="double_covariant")) + tangent = tuple( + b - a + for a, b in zip( + entity.vertices[edge[0]], entity.vertices[edge[1]] + ) + ) + dofs.append( + PointInnerProduct( + reference, + tuple( + o + + sum( + sympy.Rational(a[j] * b, order + 2) + for a, b in zip(entity.axes, i[::-1]) + ) + for j, o in enumerate(entity.origin) + ), + tangent, + tangent, + entity=(edim, e_n), + mapping="double_covariant", + ) + ) elif variant == "integral": space = Lagrange(create_reference("interval"), order, "equispaced") basis = [f.subs(x, t) for f in space.get_basis_functions()] for e_n, vs in enumerate(reference.sub_entities(1)): edge_e = reference.sub_entity(1, e_n) - tangent = tuple((b - a) / edge_e.jacobian() - for a, b in zip(edge_e.vertices[0], edge_e.vertices[1])) + tangent = tuple( + (b - a) / edge_e.jacobian() + for a, b in zip(edge_e.vertices[0], edge_e.vertices[1]) + ) for f, dof in zip(basis, space.dofs): - dofs.append(InnerProductIntegralMoment( - reference, f, tangent, tangent, dof, entity=(1, e_n), - mapping="double_covariant")) + dofs.append( + InnerProductIntegralMoment( + reference, + f, + tangent, + tangent, + dof, + entity=(1, e_n), + mapping="double_covariant", + ) + ) if reference.tdim == 2: if order > 0: dofs += make_integral_moment_dofs( reference, - cells=(IntegralMoment, Regge, order - 1, "double_covariant", - {"variant": "integral"}), + cells=( + IntegralMoment, + Regge, + order - 1, + "double_covariant", + {"variant": "integral"}, + ), ) elif reference.tdim == 3: @@ -89,22 +129,40 @@ def __init__(self, reference: Reference, order: int, variant: str = "point"): for f_n, vs in enumerate(reference.sub_entities(2)): face = reference.sub_entity(2, f_n) for f, dof in zip(basis, rspace.dofs): - dofs.append(IntegralMoment( - reference, f * face.jacobian(), dof, - entity=(2, f_n), mapping="double_covariant")) + dofs.append( + IntegralMoment( + reference, + f * face.jacobian(), + dof, + entity=(2, f_n), + mapping="double_covariant", + ) + ) if order > 1: dofs += make_integral_moment_dofs( reference, - cells=(IntegralMoment, Regge, order - 2, "double_covariant", - {"variant": "integral"}), + cells=( + IntegralMoment, + Regge, + order - 2, + "double_covariant", + {"variant": "integral"}, + ), ) else: raise ValueError(f"Unknown variant: {variant}") - super().__init__(reference, order, poly, dofs, reference.tdim, reference.tdim ** 2, - (reference.tdim, reference.tdim)) + super().__init__( + reference, + order, + poly, + dofs, + reference.tdim, + reference.tdim**2, + (reference.tdim, reference.tdim), + ) self.variant = variant def init_kwargs(self) -> typing.Dict[str, typing.Any]: @@ -149,23 +207,38 @@ def __init__(self, reference: Reference, order: int, variant: str = "integral"): for j in range(order + 2): for k in range(order + 2): poly.append( - ((x[0] ** i * x[1] ** j * x[2] ** k, 0, 0), (0, 0, 0), (0, 0, 0))) + ((x[0] ** i * x[1] ** j * x[2] ** k, 0, 0), (0, 0, 0), (0, 0, 0)) + ) poly.append( - ((0, 0, 0), (0, x[1] ** i * x[0] ** j * x[2] ** k, 0), (0, 0, 0))) + ((0, 0, 0), (0, x[1] ** i * x[0] ** j * x[2] ** k, 0), (0, 0, 0)) + ) poly.append( - ((0, 0, 0), (0, 0, 0), (0, 0, x[2] ** i * x[0] ** j * x[1] ** k))) + ((0, 0, 0), (0, 0, 0), (0, 0, x[2] ** i * x[0] ** j * x[1] ** k)) + ) for i in range(order + 1): for j in range(order + 1): for k in range(order + 2): - poly.append(((0, x[0] ** i * x[1] ** j * x[2] ** k, 0), - (x[0] ** i * x[1] ** j * x[2] ** k, 0, 0), - (0, 0, 0))) - poly.append(((0, 0, x[0] ** i * x[2] ** j * x[1] ** k), - (0, 0, 0), - (x[0] ** i * x[2] ** j * x[1] ** k, 0, 0))) - poly.append(((0, 0, 0), - (0, 0, x[1] ** i * x[2] ** j * x[0] ** k), - (0, x[1] ** i * x[2] ** j * x[0] ** k, 0))) + poly.append( + ( + (0, x[0] ** i * x[1] ** j * x[2] ** k, 0), + (x[0] ** i * x[1] ** j * x[2] ** k, 0, 0), + (0, 0, 0), + ) + ) + poly.append( + ( + (0, 0, x[0] ** i * x[2] ** j * x[1] ** k), + (0, 0, 0), + (x[0] ** i * x[2] ** j * x[1] ** k, 0, 0), + ) + ) + poly.append( + ( + (0, 0, 0), + (0, 0, x[1] ** i * x[2] ** j * x[0] ** k), + (0, x[1] ** i * x[2] ** j * x[0] ** k, 0), + ) + ) dofs: ListOfFunctionals = [] if variant == "integral": @@ -174,29 +247,52 @@ def __init__(self, reference: Reference, order: int, variant: str = "integral"): basis = [f.subs(x, t) for f in space.get_basis_functions()] for e_n, vs in enumerate(reference.sub_entities(1)): edge = reference.sub_entity(1, e_n) - tangent = tuple((b - a) / edge.jacobian() - for a, b in zip(edge.vertices[0], edge.vertices[1])) + tangent = tuple( + (b - a) / edge.jacobian() for a, b in zip(edge.vertices[0], edge.vertices[1]) + ) for f, dof in zip(basis, space.dofs): - dofs.append(InnerProductIntegralMoment( - reference, f, tangent, tangent, dof, entity=(1, e_n), - mapping="double_covariant")) + dofs.append( + InnerProductIntegralMoment( + reference, + f, + tangent, + tangent, + dof, + entity=(1, e_n), + mapping="double_covariant", + ) + ) # DOFs on faces for f_n, vs in enumerate(reference.sub_entities(2)): for i in range(order + 1): for j in range(order + 1): - dofs.append(IntegralAgainst( - reference, - ((0, x[0] ** i * x[1] ** j), (x[0] ** i * x[1] ** j, 0)), - entity=(2, f_n), mapping="double_covariant")) + dofs.append( + IntegralAgainst( + reference, + ((0, x[0] ** i * x[1] ** j), (x[0] ** i * x[1] ** j, 0)), + entity=(2, f_n), + mapping="double_covariant", + ) + ) for i in range(1, order + 1): for j in range(order + 1): - dofs.append(IntegralAgainst( - reference, ((x[1] ** i * x[0] ** j * (1 - x[1]), 0), (0, 0)), - entity=(2, f_n), mapping="double_covariant")) - dofs.append(IntegralAgainst( - reference, ((0, 0), (0, x[0] ** i * x[1] ** j * (1 - x[0]))), - entity=(2, f_n), mapping="double_covariant")) + dofs.append( + IntegralAgainst( + reference, + ((x[1] ** i * x[0] ** j * (1 - x[1]), 0), (0, 0)), + entity=(2, f_n), + mapping="double_covariant", + ) + ) + dofs.append( + IntegralAgainst( + reference, + ((0, 0), (0, x[0] ** i * x[1] ** j * (1 - x[0]))), + entity=(2, f_n), + mapping="double_covariant", + ) + ) if reference.tdim == 3: # DOFs on cell @@ -205,42 +301,79 @@ def __init__(self, reference: Reference, order: int, variant: str = "integral"): for k in range(order + 1): f = x[0] ** i * x[1] ** j * x[2] ** k * (1 - x[0]) assert isinstance(f, sympy.core.expr.Expr) - dofs.append(IntegralAgainst( - reference, ((0, 0, 0), (0, 0, f), (0, f, 0)), - entity=(3, 0), mapping="double_covariant")) + dofs.append( + IntegralAgainst( + reference, + ((0, 0, 0), (0, 0, f), (0, f, 0)), + entity=(3, 0), + mapping="double_covariant", + ) + ) f = x[1] ** i * x[0] ** j * x[2] ** k * (1 - x[1]) assert isinstance(f, sympy.core.expr.Expr) - dofs.append(IntegralAgainst( - reference, ((0, 0, f), (0, 0, 0), (f, 0, 0)), - entity=(3, 0), mapping="double_covariant")) + dofs.append( + IntegralAgainst( + reference, + ((0, 0, f), (0, 0, 0), (f, 0, 0)), + entity=(3, 0), + mapping="double_covariant", + ) + ) f = x[2] ** i * x[0] ** j * x[1] ** k * (1 - x[2]) assert isinstance(f, sympy.core.expr.Expr) - dofs.append(IntegralAgainst( - reference, ((0, f, 0), (f, 0, 0), (0, 0, 0)), - entity=(3, 0), mapping="double_covariant")) + dofs.append( + IntegralAgainst( + reference, + ((0, f, 0), (f, 0, 0), (0, 0, 0)), + entity=(3, 0), + mapping="double_covariant", + ) + ) for i in range(order + 1): for j in range(1, order + 1): for k in range(1, order + 1): f = x[0] ** i * x[1] ** j * x[2] ** k * (1 - x[1]) * (1 - x[2]) assert isinstance(f, sympy.core.expr.Expr) - dofs.append(IntegralAgainst( - reference, ((f, 0, 0), (0, 0, 0), (0, 0, 0)), - entity=(3, 0), mapping="double_covariant")) + dofs.append( + IntegralAgainst( + reference, + ((f, 0, 0), (0, 0, 0), (0, 0, 0)), + entity=(3, 0), + mapping="double_covariant", + ) + ) f = x[1] ** i * x[0] ** j * x[2] ** k * (1 - x[0]) * (1 - x[2]) assert isinstance(f, sympy.core.expr.Expr) - dofs.append(IntegralAgainst( - reference, ((0, 0, 0), (0, f, 0), (0, 0, 0)), - entity=(3, 0), mapping="double_covariant")) + dofs.append( + IntegralAgainst( + reference, + ((0, 0, 0), (0, f, 0), (0, 0, 0)), + entity=(3, 0), + mapping="double_covariant", + ) + ) f = x[2] ** i * x[0] ** j * x[1] ** k * (1 - x[0]) * (1 - x[1]) assert isinstance(f, sympy.core.expr.Expr) - dofs.append(IntegralAgainst( - reference, ((0, 0, 0), (0, 0, 0), (0, 0, f)), - entity=(3, 0), mapping="double_covariant")) + dofs.append( + IntegralAgainst( + reference, + ((0, 0, 0), (0, 0, 0), (0, 0, f)), + entity=(3, 0), + mapping="double_covariant", + ) + ) else: raise ValueError(f"Unknown variant: {variant}") - super().__init__(reference, order, poly, dofs, reference.tdim, reference.tdim ** 2, - (reference.tdim, reference.tdim)) + super().__init__( + reference, + order, + poly, + dofs, + reference.tdim, + reference.tdim**2, + (reference.tdim, reference.tdim), + ) self.variant = variant def init_kwargs(self) -> typing.Dict[str, typing.Any]: diff --git a/symfem/elements/rhct.py b/symfem/elements/rhct.py index e2f56f1c..8d5bb905 100644 --- a/symfem/elements/rhct.py +++ b/symfem/elements/rhct.py @@ -42,31 +42,61 @@ def __init__(self, reference: Reference, order: int): subs = [ (reference.vertices[0], reference.vertices[1], mid), (reference.vertices[1], reference.vertices[2], mid), - (reference.vertices[2], reference.vertices[0], mid)] + (reference.vertices[2], reference.vertices[0], mid), + ] - piece_list = [tuple(ScalarFunction(p) for _ in range(3)) - for p in [1, x[0], x[1], x[0]**2, x[0]*x[1], x[1]**2, - x[0]**3 - x[1]**3]] - piece_list.append(( - ScalarFunction(4*x[0]**3 - 3*x[0]*x[1]**2 + 2*x[0]*x[1] + 4*x[1]**2), - ScalarFunction(7*x[0]**3 + 12*x[0]**2*x[1] - 7*x[0]**2 + 9*x[0]*x[1]**2 - - 14*x[0]*x[1] + 5*x[0] + 4*x[1] - 1), - ScalarFunction(3*x[0]**3 + x[0]**2 - 2*x[1]**3 + 5*x[1]**2))) - piece_list.append(( - ScalarFunction(25*x[0]**3 - 24*x[0]*x[1]**2 + 30*x[0]*x[1] - 24*x[1]**2), - ScalarFunction(35*x[0]**3 + 33*x[0]**2*x[1] - 21*x[0]**2 - 12*x[0]*x[1]**2 - + 12*x[0] - 28*x[1]**3 - 3*x[1] - 1), - ScalarFunction(3*x[0]**3 + 21*x[0]**2*x[1] + 15*x[0]**2 - 23*x[1]**3 - 9*x[1]**2))) + piece_list = [ + tuple(ScalarFunction(p) for _ in range(3)) + for p in [1, x[0], x[1], x[0] ** 2, x[0] * x[1], x[1] ** 2, x[0] ** 3 - x[1] ** 3] + ] + piece_list.append( + ( + ScalarFunction( + 4 * x[0] ** 3 - 3 * x[0] * x[1] ** 2 + 2 * x[0] * x[1] + 4 * x[1] ** 2 + ), + ScalarFunction( + 7 * x[0] ** 3 + + 12 * x[0] ** 2 * x[1] + - 7 * x[0] ** 2 + + 9 * x[0] * x[1] ** 2 + - 14 * x[0] * x[1] + + 5 * x[0] + + 4 * x[1] + - 1 + ), + ScalarFunction(3 * x[0] ** 3 + x[0] ** 2 - 2 * x[1] ** 3 + 5 * x[1] ** 2), + ) + ) + piece_list.append( + ( + ScalarFunction( + 25 * x[0] ** 3 - 24 * x[0] * x[1] ** 2 + 30 * x[0] * x[1] - 24 * x[1] ** 2 + ), + ScalarFunction( + 35 * x[0] ** 3 + + 33 * x[0] ** 2 * x[1] + - 21 * x[0] ** 2 + - 12 * x[0] * x[1] ** 2 + + 12 * x[0] + - 28 * x[1] ** 3 + - 3 * x[1] + - 1 + ), + ScalarFunction( + 3 * x[0] ** 3 + + 21 * x[0] ** 2 * x[1] + + 15 * x[0] ** 2 + - 23 * x[1] ** 3 + - 9 * x[1] ** 2 + ), + ) + ) poly: typing.List[FunctionInput] = [] - poly += [ - PiecewiseFunction({i: j for i, j in zip(subs, p)}, 2) - for p in piece_list] + poly += [PiecewiseFunction({i: j for i, j in zip(subs, p)}, 2) for p in piece_list] poly = reference.map_polyset_from_default(poly) - super().__init__( - reference, order, poly, dofs, reference.tdim, 1 - ) + super().__init__(reference, order, poly, dofs, reference.tdim, 1) names = ["reduced Hsieh-Clough-Tocher", "rHCT"] references = ["triangle"] diff --git a/symfem/elements/rt.py b/symfem/elements/rt.py index f756c456..9ceddbab 100644 --- a/symfem/elements/rt.py +++ b/symfem/elements/rt.py @@ -32,10 +32,14 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" dofs: ListOfFunctionals = make_integral_moment_dofs( reference, - facets=(NormalIntegralMoment, Lagrange, order - 1, - {"variant": variant}), - cells=(IntegralMoment, VectorLagrange, order - 2, "contravariant", - {"variant": variant}), + facets=(NormalIntegralMoment, Lagrange, order - 1, {"variant": variant}), + cells=( + IntegralMoment, + VectorLagrange, + order - 2, + "contravariant", + {"variant": variant}, + ), ) super().__init__(reference, order, poly, dofs, reference.tdim, reference.tdim) diff --git a/symfem/elements/serendipity.py b/symfem/elements/serendipity.py index daa07764..453b2e54 100644 --- a/symfem/elements/serendipity.py +++ b/symfem/elements/serendipity.py @@ -7,12 +7,22 @@ import typing from ..finite_element import CiarletElement -from ..functionals import (IntegralMoment, ListOfFunctionals, NormalIntegralMoment, PointEvaluation, - TangentIntegralMoment) +from ..functionals import ( + IntegralMoment, + ListOfFunctionals, + NormalIntegralMoment, + PointEvaluation, + TangentIntegralMoment, +) from ..functions import FunctionInput from ..moments import make_integral_moment_dofs -from ..polynomials import (Hcurl_serendipity, Hdiv_serendipity, polynomial_set_1d, - polynomial_set_vector, serendipity_set_1d) +from ..polynomials import ( + Hcurl_serendipity, + Hdiv_serendipity, + polynomial_set_1d, + polynomial_set_vector, + serendipity_set_1d, +) from ..references import NonDefaultReferenceError, Reference from .dpc import DPC, VectorDPC @@ -82,10 +92,8 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" dofs: ListOfFunctionals = make_integral_moment_dofs( reference, edges=(TangentIntegralMoment, DPC, order, {"variant": variant}), - faces=(IntegralMoment, VectorDPC, order - 2, "covariant", - {"variant": variant}), - volumes=(IntegralMoment, VectorDPC, order - 4, "covariant", - {"variant": variant}), + faces=(IntegralMoment, VectorDPC, order - 2, "covariant", {"variant": variant}), + volumes=(IntegralMoment, VectorDPC, order - 4, "covariant", {"variant": variant}), ) super().__init__(reference, order, poly, dofs, reference.tdim, reference.tdim) @@ -127,8 +135,7 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" dofs: ListOfFunctionals = make_integral_moment_dofs( reference, facets=(NormalIntegralMoment, DPC, order, {"variant": variant}), - cells=(IntegralMoment, VectorDPC, order - 2, "contravariant", - {"variant": variant}), + cells=(IntegralMoment, VectorDPC, order - 2, "contravariant", {"variant": variant}), ) super().__init__(reference, order, poly, dofs, reference.tdim, reference.tdim) diff --git a/symfem/elements/taylor.py b/symfem/elements/taylor.py index 6cb9bfc4..45c0e240 100644 --- a/symfem/elements/taylor.py +++ b/symfem/elements/taylor.py @@ -28,8 +28,11 @@ def __init__(self, reference: Reference, order: int): ) for i in product(range(order + 1), repeat=reference.tdim): if 1 <= sum(i) <= order: - dofs.append(DerivativePointEvaluation( - reference, reference.midpoint(), i, entity=(reference.tdim, 0))) + dofs.append( + DerivativePointEvaluation( + reference, reference.midpoint(), i, entity=(reference.tdim, 0) + ) + ) poly: typing.List[FunctionInput] = [] poly += polynomial_set_1d(reference.tdim, order) diff --git a/symfem/elements/tnt.py b/symfem/elements/tnt.py index 12db68d9..99488d76 100644 --- a/symfem/elements/tnt.py +++ b/symfem/elements/tnt.py @@ -10,8 +10,14 @@ import sympy from ..finite_element import CiarletElement -from ..functionals import (DerivativeIntegralMoment, IntegralAgainst, ListOfFunctionals, - NormalIntegralMoment, PointEvaluation, TangentIntegralMoment) +from ..functionals import ( + DerivativeIntegralMoment, + IntegralAgainst, + ListOfFunctionals, + NormalIntegralMoment, + PointEvaluation, + TangentIntegralMoment, +) from ..functions import FunctionInput, ScalarFunction, VectorFunction from ..moments import make_integral_moment_dofs from ..polynomials import orthogonal_basis, quolynomial_set_1d, quolynomial_set_vector @@ -87,30 +93,31 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" for i in range(1, order + 1): f = i * t[0] ** (i - 1) for edge_n in range(reference.sub_entity_count(1)): - dofs.append(IntegralAgainst( - reference, f, entity=(1, edge_n), mapping="identity")) + dofs.append(IntegralAgainst(reference, f, entity=(1, edge_n), mapping="identity")) for i in range(1, order): for j in range(1, order): f = t[0] ** i * (t[0] - 1) * t[1] ** j * (t[1] - 1) delta_f = (f.diff(t[0]).diff(t[0]) + f.diff(t[1]).diff(t[1])).expand() for face_n in range(reference.sub_entity_count(2)): - dofs.append(IntegralAgainst( - reference, delta_f, entity=(2, face_n), mapping="identity")) + dofs.append( + IntegralAgainst(reference, delta_f, entity=(2, face_n), mapping="identity") + ) if reference.tdim == 3: dummy_dof = PointEvaluation(reference, reference.midpoint(), (3, 0)) for ii in product(range(1, order), repeat=3): f = sympy.Integer(1) for j, k in zip(ii, x): - f *= k ** j * (k - 1) + f *= k**j * (k - 1) grad_f = tuple(sympy.S(j).expand() for j in ScalarFunction(f).grad(3)) - dofs.append(DerivativeIntegralMoment( - reference, 1, grad_f, dummy_dof, entity=(3, 0), mapping="identity")) + dofs.append( + DerivativeIntegralMoment( + reference, 1, grad_f, dummy_dof, entity=(3, 0), mapping="identity" + ) + ) - super().__init__( - reference, order, poly, dofs, reference.tdim, 1 - ) + super().__init__(reference, order, poly, dofs, reference.tdim, 1) self.variant = variant def init_kwargs(self) -> typing.Dict[str, typing.Any]: @@ -147,24 +154,44 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" if reference.tdim == 2: for ii in product([0, 1], repeat=2): if sum(ii) != 0: - poly.append(tuple(sympy.S(j).expand() for j in [ - p(order, ii[0] * x[0]) * b(order + 1, ii[1] * x[1]), - -b(order + 1, ii[0] * x[0]) * p(order, ii[1] * x[1])])) + poly.append( + tuple( + sympy.S(j).expand() + for j in [ + p(order, ii[0] * x[0]) * b(order + 1, ii[1] * x[1]), + -b(order + 1, ii[0] * x[0]) * p(order, ii[1] * x[1]), + ] + ) + ) else: face_poly = [] for ii in product([0, 1], repeat=2): if sum(ii) != 0: - face_poly.append(tuple(sympy.S(j).expand() for j in [ - b(order + 1, ii[0] * t[0]) * p(order, ii[1] * t[1]), - p(order, ii[0] * t[0]) * b(order + 1, ii[1] * t[1])])) - for lamb_n in [(x[0], 0, 0), (1 - x[0], 0, 0), - (0, x[1], 0), (0, 1 - x[1], 0), - (0, 0, x[2]), (0, 0, 1 - x[2])]: + face_poly.append( + tuple( + sympy.S(j).expand() + for j in [ + b(order + 1, ii[0] * t[0]) * p(order, ii[1] * t[1]), + p(order, ii[0] * t[0]) * b(order + 1, ii[1] * t[1]), + ] + ) + ) + for lamb_n in [ + (x[0], 0, 0), + (1 - x[0], 0, 0), + (0, x[1], 0), + (0, 1 - x[1], 0), + (0, 0, x[2]), + (0, 0, 1 - x[2]), + ]: variables = tuple(i for i, j in enumerate(lamb_n) if j == 0) for pf in face_poly: psub = VectorFunction(pf).subs(t[:2], [x[j] for j in variables]) - pc = VectorFunction(lamb_n).cross(VectorFunction([ - psub[variables.index(i)] if i in variables else 0 for i in range(3)])) + pc = VectorFunction(lamb_n).cross( + VectorFunction( + [psub[variables.index(i)] if i in variables else 0 for i in range(3)] + ) + ) poly.append(pc) dofs: ListOfFunctionals = [] @@ -184,18 +211,23 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" for i in range(2, order + 1): for j in range(2, order + 1): - face_moments.append(VectorFunction(( - t[1] ** (j - 1) * (1 - t[1]) * t[0] ** (i - 2) * (i * t[0] - i + 1), - -t[0] ** (i - 1) * (1 - t[0]) * t[1] ** (j - 2) * (j - 1 - j * t[1])))) + face_moments.append( + VectorFunction( + ( + t[1] ** (j - 1) * (1 - t[1]) * t[0] ** (i - 2) * (i * t[0] - i + 1), + -(t[0] ** (i - 1)) * (1 - t[0]) * t[1] ** (j - 2) * (j - 1 - j * t[1]), + ) + ) + ) if reference.tdim == 2: for f in face_moments: - dofs.append(IntegralAgainst( - reference, f, entity=(2, 0), mapping="contravariant")) + dofs.append(IntegralAgainst(reference, f, entity=(2, 0), mapping="contravariant")) elif reference.tdim == 3: for face_n in range(6): for f in face_moments: - dofs.append(IntegralAgainst( - reference, f, entity=(2, face_n), mapping="contravariant")) + dofs.append( + IntegralAgainst(reference, f, entity=(2, face_n), mapping="contravariant") + ) # Interior Moments if reference.tdim == 3: @@ -203,20 +235,35 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" for j in range(1, order): for k in range(order + 1): f = (x[0] ** k * x[1] ** i * (1 - x[1]) * x[2] ** j * (1 - x[2]), 0, 0) - dofs.append(IntegralAgainst( - reference, VectorFunction(f).curl().curl(), entity=(3, 0), - mapping="covariant")) + dofs.append( + IntegralAgainst( + reference, + VectorFunction(f).curl().curl(), + entity=(3, 0), + mapping="covariant", + ) + ) f = (0, x[1] ** k * x[0] ** i * (1 - x[0]) * x[2] ** j * (1 - x[2]), 0) - dofs.append(IntegralAgainst( - reference, VectorFunction(f).curl().curl(), entity=(3, 0), - mapping="covariant")) + dofs.append( + IntegralAgainst( + reference, + VectorFunction(f).curl().curl(), + entity=(3, 0), + mapping="covariant", + ) + ) if k in [0, 2]: - f = (0, 0, x[2] ** k * x[0] ** i * (1 - x[0]) * x[1] ** j * (1 - x[1])) - dofs.append(IntegralAgainst( - reference, VectorFunction(f).curl().curl(), - entity=(3, 0), mapping="covariant")) + f = (0, 0, x[2] ** k * x[0] ** i * (1 - x[0]) * x[1] ** j * (1 - x[1])) + dofs.append( + IntegralAgainst( + reference, + VectorFunction(f).curl().curl(), + entity=(3, 0), + mapping="covariant", + ) + ) for i in range(2, order + 1): for j in range(2, order + 1): @@ -225,12 +272,13 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" f *= x[1] ** (j - 1) * x[1] ** j f *= x[2] ** (k - 1) * x[2] ** k grad_f = ScalarFunction(f).grad(3) - dofs.append(IntegralAgainst( - reference, grad_f, entity=(3, 0), mapping="contravariant")) + dofs.append( + IntegralAgainst( + reference, grad_f, entity=(3, 0), mapping="contravariant" + ) + ) - super().__init__( - reference, order, poly, dofs, reference.tdim, reference.tdim - ) + super().__init__(reference, order, poly, dofs, reference.tdim, reference.tdim) self.variant = variant def init_kwargs(self) -> typing.Dict[str, typing.Any]: @@ -267,21 +315,31 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" if reference.tdim == 2: for ii in product([0, 1], repeat=2): if sum(ii) != 0: - poly.append(tuple(sympy.S(j).expand() for j in [ - b(order + 1, ii[0] * x[0]) * p(order, ii[1] * x[1]), - p(order, ii[0] * x[0]) * b(order + 1, ii[1] * x[1]), - ])) + poly.append( + tuple( + sympy.S(j).expand() + for j in [ + b(order + 1, ii[0] * x[0]) * p(order, ii[1] * x[1]), + p(order, ii[0] * x[0]) * b(order + 1, ii[1] * x[1]), + ] + ) + ) else: for ii in product([0, 1], repeat=3): if sum(ii) != 0: - poly.append(( - b(order + 1, - ii[0] * x[0]) * p(order, ii[1] * x[1]) * p(order, ii[2] * x[2]), - p(order, - ii[0] * x[0]) * b(order + 1, ii[1] * x[1]) * p(order, ii[2] * x[2]), - p(order, - ii[0] * x[0]) * p(order, ii[1] * x[1]) * b(order + 1, ii[2] * x[2]), - )) + poly.append( + ( + b(order + 1, ii[0] * x[0]) + * p(order, ii[1] * x[1]) + * p(order, ii[2] * x[2]), + p(order, ii[0] * x[0]) + * b(order + 1, ii[1] * x[1]) + * p(order, ii[2] * x[2]), + p(order, ii[0] * x[0]) + * p(order, ii[1] * x[1]) + * b(order + 1, ii[2] * x[2]), + ) + ) dofs: ListOfFunctionals = [] dofs += make_integral_moment_dofs( @@ -296,55 +354,85 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" else: f = x[0] ** ii[0] * x[1] ** ii[1] * x[2] ** ii[2] grad_f = ScalarFunction(f).grad(reference.tdim) - dofs.append(IntegralAgainst( - reference, grad_f, entity=(reference.tdim, 0), mapping="covariant")) + dofs.append( + IntegralAgainst( + reference, grad_f, entity=(reference.tdim, 0), mapping="covariant" + ) + ) if reference.tdim == 2: for i in range(2, order + 1): for j in range(2, order + 1): - f = (x[0] ** (i - 1) * (1 - x[0]) * x[1] ** (j - 2) * (j - 1 - j * x[1]), - x[1] ** (j - 1) * (1 - x[1]) * x[0] ** (i - 2) * (i * x[0] - i + 1)) - dofs.append(IntegralAgainst( - reference, f, entity=(reference.tdim, 0), mapping="covariant")) + f = ( + x[0] ** (i - 1) * (1 - x[0]) * x[1] ** (j - 2) * (j - 1 - j * x[1]), + x[1] ** (j - 1) * (1 - x[1]) * x[0] ** (i - 2) * (i * x[0] - i + 1), + ) + dofs.append( + IntegralAgainst( + reference, f, entity=(reference.tdim, 0), mapping="covariant" + ) + ) if reference.tdim == 3: for i in range(2, order + 1): for j in range(2, order + 1): for k in range(order + 1): f = ( - x[2] ** k * x[0] ** (i - 1) * (1 - x[0]) * x[2] ** (j - 2) * ( - j - 1 - j * x[1]), - x[2] ** k * x[1] ** (j - 1) * (1 - x[1]) * x[0] ** (i - 2) * ( - i * x[0] - i + 1), - 0 + x[2] ** k + * x[0] ** (i - 1) + * (1 - x[0]) + * x[2] ** (j - 2) + * (j - 1 - j * x[1]), + x[2] ** k + * x[1] ** (j - 1) + * (1 - x[1]) + * x[0] ** (i - 2) + * (i * x[0] - i + 1), + 0, + ) + dofs.append( + IntegralAgainst( + reference, f, entity=(reference.tdim, 0), mapping="covariant" + ) ) - dofs.append(IntegralAgainst( - reference, f, entity=(reference.tdim, 0), - mapping="covariant")) f = ( - x[1] ** k * x[0] ** (i - 1) * (1 - x[0]) * x[2] ** (j - 2) * ( - j - 1 - j * x[2]), + x[1] ** k + * x[0] ** (i - 1) + * (1 - x[0]) + * x[2] ** (j - 2) + * (j - 1 - j * x[2]), 0, - x[1] ** k * x[2] ** (j - 1) * (1 - x[2]) * x[0] ** (i - 2) * ( - i * x[0] - i + 1) + x[1] ** k + * x[2] ** (j - 1) + * (1 - x[2]) + * x[0] ** (i - 2) + * (i * x[0] - i + 1), + ) + dofs.append( + IntegralAgainst( + reference, f, entity=(reference.tdim, 0), mapping="covariant" + ) ) - dofs.append(IntegralAgainst( - reference, f, entity=(reference.tdim, 0), - mapping="covariant")) if k in [0, 2]: f = ( 0, - x[0] ** k * x[1] ** (i - 1) * (1 - x[1]) * x[2] ** (j - 2) * ( - j - 1 - j * x[2]), - x[0] ** k * x[2] ** (j - 1) * (1 - x[2]) * x[1] ** (i - 2) * ( - i * x[1] - i + 1) + x[0] ** k + * x[1] ** (i - 1) + * (1 - x[1]) + * x[2] ** (j - 2) + * (j - 1 - j * x[2]), + x[0] ** k + * x[2] ** (j - 1) + * (1 - x[2]) + * x[1] ** (i - 2) + * (i * x[1] - i + 1), + ) + dofs.append( + IntegralAgainst( + reference, f, entity=(reference.tdim, 0), mapping="covariant" + ) ) - dofs.append(IntegralAgainst( - reference, f, entity=(reference.tdim, 0), - mapping="covariant")) - super().__init__( - reference, order, poly, dofs, reference.tdim, reference.tdim - ) + super().__init__(reference, order, poly, dofs, reference.tdim, reference.tdim) self.variant = variant def init_kwargs(self) -> typing.Dict[str, typing.Any]: diff --git a/symfem/elements/transition.py b/symfem/elements/transition.py index c67f6e97..b26c105a 100644 --- a/symfem/elements/transition.py +++ b/symfem/elements/transition.py @@ -18,10 +18,14 @@ class Transition(CiarletElement): """Transition finite element.""" - def __init__(self, reference: Reference, order: int, - edge_orders: typing.Optional[typing.List[int]] = None, - face_orders: typing.Optional[typing.List[int]] = None, - variant: str = "equispaced"): + def __init__( + self, + reference: Reference, + order: int, + edge_orders: typing.Optional[typing.List[int]] = None, + face_orders: typing.Optional[typing.List[int]] = None, + variant: str = "equispaced", + ): """Create the element. Args: @@ -88,8 +92,9 @@ def __init__(self, reference: Reference, order: int, for i, f in enumerate(bubble_space.get_basis_functions()): if i in reference.edges[e_n]: bubble *= f - space = Lagrange(entity.default_reference(), entity_order - edim - 1, - variant=variant) + space = Lagrange( + entity.default_reference(), entity_order - edim - 1, variant=variant + ) variables = [] origin = ref_entity.vertices[0] used = [] @@ -116,8 +121,11 @@ def init_kwargs(self) -> typing.Dict[str, typing.Any]: Returns: Keyword argument dictionary """ - return {"variant": self.variant, "face_orders": self.face_orders, - "edge_orders": self.edge_orders} + return { + "variant": self.variant, + "face_orders": self.face_orders, + "edge_orders": self.edge_orders, + } names = ["transition"] references = ["triangle", "tetrahedron"] diff --git a/symfem/elements/trimmed_serendipity.py b/symfem/elements/trimmed_serendipity.py index 57d3397b..8514d38c 100644 --- a/symfem/elements/trimmed_serendipity.py +++ b/symfem/elements/trimmed_serendipity.py @@ -8,8 +8,13 @@ import typing from ..finite_element import CiarletElement -from ..functionals import (IntegralAgainst, IntegralMoment, ListOfFunctionals, NormalIntegralMoment, - TangentIntegralMoment) +from ..functionals import ( + IntegralAgainst, + IntegralMoment, + ListOfFunctionals, + NormalIntegralMoment, + TangentIntegralMoment, +) from ..functions import FunctionInput, ScalarFunction, VectorFunction from ..moments import make_integral_moment_dofs from ..polynomials import polynomial_set_vector @@ -36,37 +41,47 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" poly += polynomial_set_vector(reference.tdim, reference.tdim, order - 1) if reference.tdim == 2: poly += [ - (x[0] ** j * x[1] ** (order - j), -x[0] ** (j + 1) * x[1] ** (order - 1 - j)) - for j in range(order)] + (x[0] ** j * x[1] ** (order - j), -(x[0] ** (j + 1)) * x[1] ** (order - 1 - j)) + for j in range(order) + ] if order == 1: poly += [(x[1], x[0])] else: - poly += [(x[1] ** order, order * x[0] * x[1] ** (order - 1)), - (order * x[0] ** (order - 1) * x[1], x[0] ** order)] + poly += [ + (x[1] ** order, order * x[0] * x[1] ** (order - 1)), + (order * x[0] ** (order - 1) * x[1], x[0] ** order), + ] else: for i in range(order): for j in range(order - i): for dim in range(3): if i == 0 or dim != 0: - p = VectorFunction(tuple(x)).cross(VectorFunction([ - x[0] ** i * x[1] ** j * x[2] ** (order - 1 - i - j) - if d == dim else 0 for d in range(3)])) + p = VectorFunction(tuple(x)).cross( + VectorFunction( + [ + x[0] ** i * x[1] ** j * x[2] ** (order - 1 - i - j) + if d == dim + else 0 + for d in range(3) + ] + ) + ) poly.append(p) if order == 1: poly += [ScalarFunction(x[0] * x[1] * x[2] ** order).grad(3)] else: - poly += [ScalarFunction(x[0] * x[1] * x[2] ** order).grad(3), - ScalarFunction(x[0] * x[1] ** order * x[2]).grad(3), - ScalarFunction(x[0] ** order * x[1] * x[2]).grad(3)] + poly += [ + ScalarFunction(x[0] * x[1] * x[2] ** order).grad(3), + ScalarFunction(x[0] * x[1] ** order * x[2]).grad(3), + ScalarFunction(x[0] ** order * x[1] * x[2]).grad(3), + ] for i in range(order + 1): poly.append(ScalarFunction(x[0] * x[1] ** i * x[2] ** (order - i)).grad(3)) if i != 1: - poly.append( - ScalarFunction(x[1] * x[0] ** i * x[2] ** (order - i)).grad(3)) + poly.append(ScalarFunction(x[1] * x[0] ** i * x[2] ** (order - i)).grad(3)) if order - i != 1: - poly.append( - ScalarFunction(x[2] * x[0] ** i * x[1] ** (order - i)).grad(3)) + poly.append(ScalarFunction(x[2] * x[0] ** i * x[1] ** (order - i)).grad(3)) for i in range(order): p = x[0] * x[1] ** i * x[2] ** (order - 1 - i) poly.append((0, -x[2] * p, x[1] * p)) @@ -80,22 +95,19 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" dofs += make_integral_moment_dofs( reference, edges=(TangentIntegralMoment, DPC, order - 1, {"variant": variant}), - faces=(IntegralMoment, VectorDPC, order - 3, "contravariant", - {"variant": variant}), - volumes=(IntegralMoment, VectorDPC, order - 5, "contravariant", - {"variant": variant}), + faces=(IntegralMoment, VectorDPC, order - 3, "contravariant", {"variant": variant}), + volumes=(IntegralMoment, VectorDPC, order - 5, "contravariant", {"variant": variant}), ) if order >= 2: for f_n in range(reference.sub_entity_count(2)): for i in range(order): f = ScalarFunction(x[0] ** (order - 1 - i) * x[1] ** i).grad(2) f2 = VectorFunction((f[1], -f[0])).subs(x, tuple(t)) - dofs.append(IntegralAgainst( - reference, f2, entity=(2, f_n), mapping="contravariant")) + dofs.append( + IntegralAgainst(reference, f2, entity=(2, f_n), mapping="contravariant") + ) - super().__init__( - reference, order, poly, dofs, reference.tdim, reference.tdim - ) + super().__init__(reference, order, poly, dofs, reference.tdim, reference.tdim) self.variant = variant def init_kwargs(self) -> typing.Dict[str, typing.Any]: @@ -132,12 +144,15 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" if reference.tdim == 2: poly += [ (x[0] ** (j + 1) * x[1] ** (order - 1 - j), x[0] ** j * x[1] ** (order - j)) - for j in range(order)] + for j in range(order) + ] if order == 1: poly += [(x[0], -x[1])] else: - poly += [(order * x[0] * x[1] ** (order - 1), -x[1] ** order), - (-x[0] ** order, order * x[0] ** (order - 1) * x[1])] + poly += [ + (order * x[0] * x[1] ** (order - 1), -(x[1] ** order)), + (-(x[0] ** order), order * x[0] ** (order - 1) * x[1]), + ] else: for i in range(order): for j in range(order - i): @@ -145,38 +160,38 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" poly.append((x[0] * p, x[1] * p, x[2] * p)) for i in range(order): p = x[0] * x[1] ** i * x[2] ** (order - 1 - i) - poly.append( - VectorFunction((0, -x[2] * p, x[1] * p)).curl()) + poly.append(VectorFunction((0, -x[2] * p, x[1] * p)).curl()) p = x[1] * x[0] ** i * x[2] ** (order - 1 - i) - poly.append( - VectorFunction((-x[2] * p, 0, x[0] * p)).curl()) + poly.append(VectorFunction((-x[2] * p, 0, x[0] * p)).curl()) if order > 1: p = x[2] * x[0] ** i * x[1] ** (order - 1 - i) - poly.append( - VectorFunction((-x[1] * p, x[0] * p, 0)).curl()) + poly.append(VectorFunction((-x[1] * p, x[0] * p, 0)).curl()) dofs: ListOfFunctionals = [] dofs += make_integral_moment_dofs( reference, facets=(NormalIntegralMoment, DPC, order - 1, {"variant": variant}), - cells=(IntegralMoment, VectorDPC, order - 3, "covariant", - {"variant": variant}), + cells=(IntegralMoment, VectorDPC, order - 3, "covariant", {"variant": variant}), ) if order >= 2: if reference.tdim == 2: - fs = [ScalarFunction(x[0] ** (order - 1 - i) * x[1] ** i).grad(2) - for i in range(order)] + fs = [ + ScalarFunction(x[0] ** (order - 1 - i) * x[1] ** i).grad(2) + for i in range(order) + ] else: - fs = [ScalarFunction(x[0] ** (order - 1 - i - j) * x[1] ** i * x[2] ** j).grad(3) - for i in range(order) for j in range(order - i)] + fs = [ + ScalarFunction(x[0] ** (order - 1 - i - j) * x[1] ** i * x[2] ** j).grad(3) + for i in range(order) + for j in range(order - i) + ] for f in fs: f2 = f.subs(x, tuple(t)) - dofs.append(IntegralAgainst(reference, f2, entity=(reference.tdim, 0), - mapping="covariant")) + dofs.append( + IntegralAgainst(reference, f2, entity=(reference.tdim, 0), mapping="covariant") + ) - super().__init__( - reference, order, poly, dofs, reference.tdim, reference.tdim - ) + super().__init__(reference, order, poly, dofs, reference.tdim, reference.tdim) self.variant = variant def init_kwargs(self) -> typing.Dict[str, typing.Any]: diff --git a/symfem/elements/vector_enriched_galerkin.py b/symfem/elements/vector_enriched_galerkin.py index 4be374df..dc228927 100644 --- a/symfem/elements/vector_enriched_galerkin.py +++ b/symfem/elements/vector_enriched_galerkin.py @@ -29,8 +29,11 @@ def __init__(self, reference: Reference): f = VectorFunction(tuple(x[i] - j for i, j in enumerate(reference.midpoint()))) poly: typing.List[FunctionInput] = [f] size = f.dot(f).integral(reference, x) - dofs: typing.List[BaseFunctional] = [IntegralAgainst( - reference, tuple(i / size for i in f), (reference.tdim, 0), "contravariant")] + dofs: typing.List[BaseFunctional] = [ + IntegralAgainst( + reference, tuple(i / size for i in f), (reference.tdim, 0), "contravariant" + ) + ] super().__init__(reference, 1, poly, dofs, reference.tdim, reference.tdim) diff --git a/symfem/elements/wu_xu.py b/symfem/elements/wu_xu.py index 0be3ebec..dc9a212f 100644 --- a/symfem/elements/wu_xu.py +++ b/symfem/elements/wu_xu.py @@ -7,8 +7,12 @@ import typing from ..finite_element import CiarletElement -from ..functionals import (DerivativePointEvaluation, IntegralOfDirectionalMultiderivative, - ListOfFunctionals, PointEvaluation) +from ..functionals import ( + DerivativePointEvaluation, + IntegralOfDirectionalMultiderivative, + ListOfFunctionals, + PointEvaluation, +) from ..functions import FunctionInput from ..polynomials import polynomial_set_1d from ..references import NonDefaultReferenceError, Reference @@ -25,11 +29,11 @@ def derivatives(dim: int, order: int) -> typing.List[typing.Tuple[int, ...]]: List of derivative order tuples """ if dim == 1: - return [(order, )] + return [(order,)] out = [] for i in range(order + 1): - out += [(i, ) + j for j in derivatives(dim - 1, order - i)] + out += [(i,) + j for j in derivatives(dim - 1, order - i)] return out @@ -64,9 +68,14 @@ def __init__(self, reference: Reference, order: int): for v_n, v in enumerate(reference.vertices): dofs.append(PointEvaluation(reference, v, entity=(0, v_n))) for i in range(reference.tdim): - dofs.append(DerivativePointEvaluation( - reference, v, tuple(1 if i == j else 0 for j in range(reference.tdim)), - entity=(0, v_n))) + dofs.append( + DerivativePointEvaluation( + reference, + v, + tuple(1 if i == j else 0 for j in range(reference.tdim)), + entity=(0, v_n), + ) + ) for codim in range(1, reference.tdim): dim = reference.tdim - codim for e_n, vs in enumerate(reference.sub_entities(codim=codim)): @@ -83,9 +92,11 @@ def __init__(self, reference: Reference, order: int): else: raise NotImplementedError for orders in derivatives(len(normals), len(normals)): - dofs.append(IntegralOfDirectionalMultiderivative( - reference, tuple(normals), orders, (dim, e_n), - scale=1 / volume)) + dofs.append( + IntegralOfDirectionalMultiderivative( + reference, tuple(normals), orders, (dim, e_n), scale=1 / volume + ) + ) super().__init__(reference, order, poly, dofs, reference.tdim, 1) diff --git a/symfem/finite_element.py b/symfem/finite_element.py index 708011ca..12b7333e 100644 --- a/symfem/finite_element.py +++ b/symfem/finite_element.py @@ -9,19 +9,24 @@ import sympy -from .basis_functions import BasisFunction -from .caching import load_cached_matrix, save_cached_matrix -from .functionals import ListOfFunctionals -from .functions import (AnyFunction, FunctionInput, ScalarFunction, VectorFunction, - parse_function_input) -from .geometry import PointType, SetOfPointsInput, parse_set_of_points_input -from .mappings import MappingNotImplemented -from .piecewise_functions import PiecewiseFunction -from .plotting import Picture, colors -from .references import NonDefaultReferenceError, Reference -from .symbols import x -from .utils import allequal -from .version import version +from symfem.basis_functions import BasisFunction +from symfem.caching import load_cached_matrix, save_cached_matrix +from symfem.functionals import ListOfFunctionals +from symfem.functions import ( + AnyFunction, + FunctionInput, + ScalarFunction, + VectorFunction, + parse_function_input, +) +from symfem.geometry import PointType, SetOfPointsInput, parse_set_of_points_input +from symfem.mappings import MappingNotImplemented +from symfem.piecewise_functions import PiecewiseFunction +from symfem.plotting import Picture, colors +from symfem.references import NonDefaultReferenceError, Reference +from symfem.symbols import x +from symfem.utils import allequal +from symfem.version import version TabulatedBasis = typing.Union[ typing.List[typing.Union[sympy.core.expr.Expr, int]], @@ -46,8 +51,13 @@ class FiniteElement(ABC): _value_scale: typing.Union[None, sympy.core.expr.Expr] def __init__( - self, reference: Reference, order: int, space_dim: int, domain_dim: int, range_dim: int, - range_shape: typing.Optional[typing.Tuple[int, ...]] = None + self, + reference: Reference, + order: int, + space_dim: int, + domain_dim: int, + range_dim: int, + range_shape: typing.Optional[typing.Tuple[int, ...]] = None, ): """Create a finite element. @@ -93,8 +103,10 @@ def dof_entities(self) -> typing.List[typing.Tuple[int, int]]: """ def plot_dof_diagram( - self, filename: typing.Union[str, typing.List[str]], - plot_options: typing.Dict[str, typing.Any] = {}, **kwargs: typing.Any + self, + filename: typing.Union[str, typing.List[str]], + plot_options: typing.Dict[str, typing.Any] = {}, + **kwargs: typing.Any, ): """Plot a diagram showing the DOFs of the element. @@ -121,8 +133,10 @@ def plot_dof_diagram( img.add_fill(pts, colors.WHITE, 0.5) for dim, e in entities: - dofs = [(dof_positions[i], dof_directions[i], dof_entities[i], i) - for i in self.entity_dofs(dim, e)] + dofs = [ + (dof_positions[i], dof_directions[i], dof_entities[i], i) + for i in self.entity_dofs(dim, e) + ] dofs.sort(key=lambda d: img.z(d[0])) for d in dofs: direction = d[1] @@ -132,11 +146,9 @@ def plot_dof_diagram( if d != d2 and d[0] == p: shifted = True break - img.add_dof_arrow(d[0], direction, d[3], - colors.entity(d[2][0]), shifted) + img.add_dof_arrow(d[0], direction, d[3], colors.entity(d[2][0]), shifted) else: - img.add_dof_marker( - d[0], d[3], colors.entity(d[2][0])) + img.add_dof_marker(d[0], d[3], colors.entity(d[2][0])) img.save(filename, plot_options=plot_options) @@ -177,7 +189,9 @@ def get_basis_function(self, n: int) -> BasisFunction: return ElementBasisFunction(self, n) def tabulate_basis( - self, points_in: SetOfPointsInput, order: str = "xyzxyz", + self, + points_in: SetOfPointsInput, + order: str = "xyzxyz", ) -> TabulatedBasis: """Evaluate the basis functions of the element at the given points. @@ -189,8 +203,9 @@ def tabulate_basis( The tabulated basis functions """ points = parse_set_of_points_input(points_in) - tabbed = [tuple(b.subs(x, p).as_sympy() for b in self.get_basis_functions()) - for p in points] + tabbed = [ + tuple(b.subs(x, p).as_sympy() for b in self.get_basis_functions()) for p in points + ] if self.range_dim == 1: return tabbed @@ -231,8 +246,11 @@ def tabulate_basis_float(self, points_in: SetOfPointsInput) -> TabulatedBasis: return [tuple(b.subs(x, p).as_sympy() for b in self._float_basis_functions) for p in points] def plot_basis_function( - self, n: int, filename: typing.Union[str, typing.List[str]], - cell: typing.Optional[Reference] = None, **kwargs: typing.Any + self, + n: int, + filename: typing.Union[str, typing.List[str]], + cell: typing.Optional[Reference] = None, + **kwargs: typing.Any, ): """Plot a diagram showing a basis function. @@ -262,10 +280,11 @@ def plot_basis_function( @abstractmethod def map_to_cell( - self, vertices_in: SetOfPointsInput, + self, + vertices_in: SetOfPointsInput, basis: typing.Optional[typing.List[AnyFunction]] = None, forward_map: typing.Optional[PointType] = None, - inverse_map: typing.Optional[PointType] = None + inverse_map: typing.Optional[PointType] = None, ) -> typing.List[AnyFunction]: """Map the basis onto a cell using the appropriate mapping for the element. @@ -304,37 +323,71 @@ def test_continuity(self): # Test continuity if self.reference.name == "interval": - vertices = ((-1, ), (0, )) + vertices = ((-1,), (0,)) entity_pairs = [[0, (0, 1)]] elif self.reference.name == "triangle": vertices = ((-1, 0), (0, 0), (0, 1)) entity_pairs = [[0, (0, 1)], [0, (2, 2)], [1, (1, 0)]] elif self.reference.name == "tetrahedron": vertices = ((-1, 0, 0), (0, 0, 0), (0, 1, 0), (0, 0, 1)) - entity_pairs = [[0, (0, 1)], [0, (2, 2)], [0, (3, 3)], - [1, (0, 0)], [1, (3, 1)], [1, (4, 2)], - [2, (1, 0)]] + entity_pairs = [ + [0, (0, 1)], + [0, (2, 2)], + [0, (3, 3)], + [1, (0, 0)], + [1, (3, 1)], + [1, (4, 2)], + [2, (1, 0)], + ] elif self.reference.name == "quadrilateral": vertices = ((0, 0), (0, 1), (-1, 0), (-1, 1)) entity_pairs = [[0, (0, 0)], [0, (2, 1)], [1, (1, 0)]] elif self.reference.name == "hexahedron": - vertices = ((0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), - (-1, 0, 0), (-1, 0, 1), (-1, 1, 0), (-1, 1, 1)) - entity_pairs = [[0, (0, 0)], [0, (2, 2)], [0, (4, 1)], [0, (6, 3)], - [1, (1, 1)], [1, (2, 0)], [1, (6, 5)], [1, (9, 3)], - [2, (0, 2)]] + vertices = ( + (0, 0, 0), + (0, 0, 1), + (0, 1, 0), + (0, 1, 1), + (-1, 0, 0), + (-1, 0, 1), + (-1, 1, 0), + (-1, 1, 1), + ) + entity_pairs = [ + [0, (0, 0)], + [0, (2, 2)], + [0, (4, 1)], + [0, (6, 3)], + [1, (1, 1)], + [1, (2, 0)], + [1, (6, 5)], + [1, (9, 3)], + [2, (0, 2)], + ] elif self.reference.name == "prism": - vertices = ((-1, 0, 0), (0, 0, 0), (0, 1, 0), - (-1, 0, 1), (0, 0, 1), (0, 1, 1)) - entity_pairs = [[0, (0, 1)], [0, (2, 2)], [0, (3, 4)], [0, (5, 5)], - [1, (1, 3)], [1, (2, 4)], [1, (6, 6)], [1, (7, 8)], - [2, (2, 3)]] + vertices = ((-1, 0, 0), (0, 0, 0), (0, 1, 0), (-1, 0, 1), (0, 0, 1), (0, 1, 1)) + entity_pairs = [ + [0, (0, 1)], + [0, (2, 2)], + [0, (3, 4)], + [0, (5, 5)], + [1, (1, 3)], + [1, (2, 4)], + [1, (6, 6)], + [1, (7, 8)], + [2, (2, 3)], + ] elif self.reference.name == "pyramid": - vertices = ((-1, 0, 0), (0, 0, 0), (-1, 1, 0), - (0, 1, 0), (0, 0, 1)) - entity_pairs = [[0, (0, 1)], [0, (2, 3)], [0, (4, 4)], - [1, (1, 3)], [1, (2, 4)], [1, (6, 7)], - [2, (2, 3)]] + vertices = ((-1, 0, 0), (0, 0, 0), (-1, 1, 0), (0, 1, 0), (0, 0, 1)) + entity_pairs = [ + [0, (0, 1)], + [0, (2, 3)], + [0, (4, 4)], + [1, (1, 3)], + [1, (2, 4)], + [1, (6, 7)], + [2, (2, 3)], + ] if continuity == "L2": return @@ -360,8 +413,8 @@ def get_piece(f, point): return f if self.reference.tdim == 1: - f = get_piece(f, (0, )) - g = get_piece(g, (0, )) + f = get_piece(f, (0,)) + g = get_piece(g, (0,)) elif self.reference.tdim == 2: f = get_piece(f, (0, sympy.Rational(1, 2))) g = get_piece(g, (0, sympy.Rational(1, 2))) @@ -376,9 +429,9 @@ def get_piece(f, point): f = [f] g = [g] for _ in range(order): - deriv_f = [d.diff(i) for d in deriv_f for i in x[:self.reference.tdim]] + deriv_f = [d.diff(i) for d in deriv_f for i in x[: self.reference.tdim]] f += deriv_f - deriv_g = [d.diff(i) for d in deriv_g for i in x[:self.reference.tdim]] + deriv_g = [d.diff(i) for d in deriv_g for i in x[: self.reference.tdim]] g += deriv_g f = [i.subs(x[0], 0) for i in f] @@ -409,12 +462,16 @@ def get_piece(f, point): v0 = self.reference.vertices[vs[0]] v1 = self.reference.vertices[vs[1]] tangent = VectorFunction(v1) - VectorFunction(v0) - f = sum(i * f[ni, nj] * j - for ni, i in enumerate(tangent) - for nj, j in enumerate(tangent)) - g = sum(i * g[ni, nj] * j - for ni, i in enumerate(tangent) - for nj, j in enumerate(tangent)) + f = sum( + i * f[ni, nj] * j + for ni, i in enumerate(tangent) + for nj, j in enumerate(tangent) + ) + g = sum( + i * g[ni, nj] * j + for ni, i in enumerate(tangent) + for nj, j in enumerate(tangent) + ) else: assert dim == 2 f = [f[1, 1], f[2, 2]] @@ -428,7 +485,7 @@ def get_piece(f, point): assert allequal(f, g) def get_tensor_factorisation( - self + self, ) -> typing.List[typing.Tuple[str, typing.List[FiniteElement], typing.List[int]]]: """Get the representation of the element as a tensor product. @@ -447,8 +504,10 @@ def _get_basis_functions_tensor(self) -> typing.List[AnyFunction]: basis = {} for t_type, factors, perm in factorisation: if t_type == "scalar": - tensor_bases = [[i.subs(x[0], x_i) for i in f.get_basis_functions()] - for x_i, f in zip(x, factors)] + tensor_bases = [ + [i.subs(x[0], x_i) for i in f.get_basis_functions()] + for x_i, f in zip(x, factors) + ] for p, k in zip(perm, product(*tensor_bases)): basis[p] = ScalarFunction(1) for i in k: @@ -485,9 +544,14 @@ class CiarletElement(FiniteElement): """Finite element defined using the Ciarlet definition.""" def __init__( - self, reference: Reference, order: int, basis: typing.List[FunctionInput], - dofs: ListOfFunctionals, domain_dim: int, range_dim: int, - range_shape: typing.Optional[typing.Tuple[int, ...]] = None + self, + reference: Reference, + order: int, + basis: typing.List[FunctionInput], + dofs: ListOfFunctionals, + domain_dim: int, + range_dim: int, + range_shape: typing.Optional[typing.Tuple[int, ...]] = None, ): """Create a Ciarlet element. @@ -575,8 +639,10 @@ def get_dual_matrix( The dual matrix """ if caching and self.cache: - cid = (f"{self.__class__.__name__} {self.order} {self.reference.vertices} " - f"{self.init_kwargs()} {self.last_updated}") + cid = ( + f"{self.__class__.__name__} {self.order} {self.reference.vertices} " + f"{self.init_kwargs()} {self.last_updated}" + ) matrix_type = "dualinv" if inverse else "dual" mat = load_cached_matrix(matrix_type, cid, (len(self.dofs), len(self.dofs))) if mat is None: @@ -584,8 +650,12 @@ def get_dual_matrix( save_cached_matrix(matrix_type, cid, mat) return mat else: - mat = sympy.Matrix([[d.eval_symbolic(b).as_sympy() for d in self.dofs] - for b in self.get_polynomial_basis()]) + mat = sympy.Matrix( + [ + [d.eval_symbolic(b).as_sympy() for d in self.dofs] + for b in self.get_polynomial_basis() + ] + ) if inverse: return mat.inv("LU") else: @@ -623,8 +693,11 @@ def get_basis_functions( return self._basis_functions def plot_basis_function( - self, n: int, filename: typing.Union[str, typing.List[str]], - cell: typing.Optional[Reference] = None, **kwargs: typing.Any + self, + n: int, + filename: typing.Union[str, typing.List[str]], + cell: typing.Optional[Reference] = None, + **kwargs: typing.Any, ): """Plot a diagram showing a basis function. @@ -649,14 +722,23 @@ def plot_basis_function( f = self.get_basis_functions()[n] d = self.dofs[n] assert self._value_scale is not None - f.plot(self.reference, filename, d.dof_point(), d.dof_direction(), d.entity, n, - self._value_scale, **kwargs) + f.plot( + self.reference, + filename, + d.dof_point(), + d.dof_direction(), + d.entity, + n, + self._value_scale, + **kwargs, + ) def map_to_cell( - self, vertices_in: SetOfPointsInput, + self, + vertices_in: SetOfPointsInput, basis: typing.Optional[typing.List[AnyFunction]] = None, forward_map: typing.Optional[PointType] = None, - inverse_map: typing.Optional[PointType] = None + inverse_map: typing.Optional[PointType] = None, ) -> typing.List[AnyFunction]: """Map the basis onto a cell using the appropriate mapping for the element. @@ -693,8 +775,8 @@ def map_to_cell( dofs_by_type[t].append(d) for ds in dofs_by_type.values(): mapped_dofs = self.dofs[ds[0]].perform_mapping( - [basis[d] for d in ds], - forward_map, inverse_map) + [basis[d] for d in ds], forward_map, inverse_map + ) for d_n, mdof in zip(ds, mapped_dofs): functions[d_n] = mdof @@ -705,7 +787,8 @@ def map_to_cell( return functions except MappingNotImplemented: element = self.__class__( - self.reference.__class__(vertices=vertices), self.order, **self.init_kwargs()) + self.reference.__class__(vertices=vertices), self.order, **self.init_kwargs() + ) return element.get_basis_functions() def test(self): @@ -753,9 +836,14 @@ class DirectElement(FiniteElement): _basis_functions: typing.List[AnyFunction] def __init__( - self, reference: Reference, order: int, basis_functions: typing.List[FunctionInput], + self, + reference: Reference, + order: int, + basis_functions: typing.List[FunctionInput], basis_entities: typing.List[typing.Tuple[int, int]], - domain_dim: int, range_dim: int, range_shape: typing.Optional[typing.Tuple[int, ...]] = None + domain_dim: int, + range_dim: int, + range_shape: typing.Optional[typing.Tuple[int, ...]] = None, ): """Create a direct element. @@ -768,8 +856,7 @@ def __init__( range_dim: The dimension of the range range_shape: The shape of the range """ - super().__init__(reference, order, len(basis_functions), domain_dim, range_dim, - range_shape) + super().__init__(reference, order, len(basis_functions), domain_dim, range_dim, range_shape) self._basis_entities = basis_entities self._basis_functions = [parse_function_input(f) for f in basis_functions] @@ -801,9 +888,12 @@ def dof_plot_positions(self) -> typing.List[PointType]: assert entity_n == 0 positions.append(sub_ref.vertices[0]) elif dim == 1: - positions.append(tuple( - o + sympy.Rational((entity_n + 1) * a, dof_count + 1) - for o, a in zip(sub_ref.origin, *sub_ref.axes))) + positions.append( + tuple( + o + sympy.Rational((entity_n + 1) * a, dof_count + 1) + for o, a in zip(sub_ref.origin, *sub_ref.axes) + ) + ) elif dim == 2: ne = 1 while ne * (ne + 1) // 2 < dof_count: @@ -812,9 +902,12 @@ def dof_plot_positions(self) -> typing.List[PointType]: while entity_n >= ne - i: entity_n -= ne - i i += 1 - positions.append(tuple( - o + sympy.Rational((entity_n + 1) * a + (i + 1) * b, ne + 1) - for o, a, b in zip(sub_ref.origin, *sub_ref.axes))) + positions.append( + tuple( + o + sympy.Rational((entity_n + 1) * a + (i + 1) * b, ne + 1) + for o, a, b in zip(sub_ref.origin, *sub_ref.axes) + ) + ) elif dim == 3: ne = 1 while ne * (ne + 1) * (ne + 2) // 6 < n: @@ -827,9 +920,12 @@ def dof_plot_positions(self) -> typing.List[PointType]: while entity_n >= ne - j: entity_n -= ne - j j += 1 - positions.append(tuple( - o + sympy.Rational((entity_n + 1) * a + (j + 1) * b + (i + 1) * c, n + 1) - for o, a, b, c in zip(sub_ref.origin, *sub_ref.axes))) + positions.append( + tuple( + o + sympy.Rational((entity_n + 1) * a + (j + 1) * b + (i + 1) * c, n + 1) + for o, a, b, c in zip(sub_ref.origin, *sub_ref.axes) + ) + ) return positions @@ -866,10 +962,11 @@ def get_basis_functions( return self._basis_functions def map_to_cell( - self, vertices_in: SetOfPointsInput, + self, + vertices_in: SetOfPointsInput, basis: typing.Optional[typing.List[AnyFunction]] = None, forward_map: typing.Optional[PointType] = None, - inverse_map: typing.Optional[PointType] = None + inverse_map: typing.Optional[PointType] = None, ) -> typing.List[AnyFunction]: """Map the basis onto a cell using the appropriate mapping for the element. @@ -954,7 +1051,8 @@ class EnrichedElement(FiniteElement): _basis_functions: typing.Optional[typing.List[AnyFunction]] def __init__( - self, subelements: typing.List[FiniteElement], + self, + subelements: typing.List[FiniteElement], ): """Create an enriched element. @@ -974,8 +1072,14 @@ def __init__( self._basis_functions = None self._subelements = subelements - super().__init__(reference, order, sum(e.space_dim for e in subelements), - domain_dim, range_dim, range_shape) + super().__init__( + reference, + order, + sum(e.space_dim for e in subelements), + domain_dim, + range_dim, + range_shape, + ) def entity_dofs(self, entity_dim: int, entity_number: int) -> typing.List[int]: """Get the numbers of the DOFs associated with the given entity. @@ -1049,10 +1153,11 @@ def get_basis_functions( return self._basis_functions def map_to_cell( - self, vertices_in: SetOfPointsInput, + self, + vertices_in: SetOfPointsInput, basis: typing.Optional[typing.List[AnyFunction]] = None, forward_map: typing.Optional[PointType] = None, - inverse_map: typing.Optional[PointType] = None + inverse_map: typing.Optional[PointType] = None, ) -> typing.List[AnyFunction]: """Map the basis onto a cell using the appropriate mapping for the element. diff --git a/symfem/functionals.py b/symfem/functionals.py index 947deb76..8da05f2c 100644 --- a/symfem/functionals.py +++ b/symfem/functionals.py @@ -5,13 +5,18 @@ import sympy -from . import mappings -from .functions import (AnyFunction, FunctionInput, ScalarFunction, VectorFunction, - parse_function_input) -from .geometry import PointType, SetOfPoints -from .piecewise_functions import PiecewiseFunction -from .references import Interval, NonDefaultReferenceError, Reference -from .symbols import t, x +from symfem import mappings +from symfem.functions import ( + AnyFunction, + FunctionInput, + ScalarFunction, + VectorFunction, + parse_function_input, +) +from symfem.geometry import PointType, SetOfPoints +from symfem.piecewise_functions import PiecewiseFunction +from symfem.references import Interval, NonDefaultReferenceError, Reference +from symfem.symbols import t, x ScalarValueOrFloat = typing.Union[sympy.core.expr.Expr, float] @@ -55,8 +60,9 @@ def _nth(n: int) -> str: class BaseFunctional(ABC): """A functional.""" - def __init__(self, reference: Reference, entity: typing.Tuple[int, int], - mapping: typing.Union[str, None]): + def __init__( + self, reference: Reference, entity: typing.Tuple[int, int], mapping: typing.Union[str, None] + ): """Create the functional. Args: @@ -121,7 +127,7 @@ def entity_definition(self) -> str: return "\\(R\\) is the reference element" else: desc = f"\\({self.entity_tex()}\\) is the {_nth(self.entity[1])} " - desc += ['vertex', 'edge', 'face', 'volume'][self.entity[0]] + desc += ["vertex", "edge", "face", "volume"][self.entity[0]] return desc def dof_direction(self) -> typing.Union[PointType, None]: @@ -132,9 +138,9 @@ def dof_direction(self) -> typing.Union[PointType, None]: """ return None - def eval(self, function: AnyFunction, symbolic: bool = True) -> typing.Union[ - ScalarFunction, float - ]: + def eval( + self, function: AnyFunction, symbolic: bool = True + ) -> typing.Union[ScalarFunction, float]: """Apply to the functional to a function. Args: @@ -170,9 +176,7 @@ def adjusted_dof_point(self) -> PointType: return point midpoint = self.reference.sub_entity(*self.entity).midpoint() - return tuple( - m + sympy.Rational(7, 10) * (p - m) - for m, p in zip(midpoint, point)) + return tuple(m + sympy.Rational(7, 10) * (p - m) for m, p in zip(midpoint, point)) def eval_symbolic(self, function: AnyFunction) -> ScalarFunction: """Symbolically apply the functional to a function. @@ -217,8 +221,13 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: class PointEvaluation(BaseFunctional): """A point evaluation.""" - def __init__(self, reference: Reference, point_in: FunctionInput, - entity: typing.Tuple[int, int], mapping: typing.Union[str, None] = "identity"): + def __init__( + self, + reference: Reference, + point_in: FunctionInput, + entity: typing.Tuple[int, int], + mapping: typing.Union[str, None] = "identity", + ): """Create the functional. Args: @@ -275,8 +284,14 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: class WeightedPointEvaluation(BaseFunctional): """A point evaluation.""" - def __init__(self, reference: Reference, point_in: FunctionInput, weight: sympy.core.expr.Expr, - entity: typing.Tuple[int, int], mapping: typing.Union[str, None] = "identity"): + def __init__( + self, + reference: Reference, + point_in: FunctionInput, + weight: sympy.core.expr.Expr, + entity: typing.Tuple[int, int], + mapping: typing.Union[str, None] = "identity", + ): """Create the functional. Args: @@ -327,8 +342,10 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: Representation of the functional as TeX, and list of terms involved """ assert isinstance(self.point, VectorFunction) - return (f"v\\mapsto {_to_tex(self.weight)} " - f"v({','.join([_to_tex(i, True) for i in self.point])})"), [] + return ( + f"v\\mapsto {_to_tex(self.weight)} " + f"v({','.join([_to_tex(i, True) for i in self.point])})" + ), [] name = "Weighted point evaluation" @@ -336,9 +353,14 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: class DerivativePointEvaluation(BaseFunctional): """A point evaluation of a given derivative.""" - def __init__(self, reference: Reference, point_in: FunctionInput, - derivative: typing.Tuple[int, ...], - entity: typing.Tuple[int, int], mapping: typing.Optional[str] = None): + def __init__( + self, + reference: Reference, + point_in: FunctionInput, + derivative: typing.Tuple[int, ...], + entity: typing.Tuple[int, int], + mapping: typing.Optional[str] = None, + ): """Create the functional. Args: @@ -431,8 +453,14 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: class PointDirectionalDerivativeEvaluation(BaseFunctional): """A point evaluation of a derivative in a fixed direction.""" - def __init__(self, reference: Reference, point_in: FunctionInput, direction_in: FunctionInput, - entity: typing.Tuple[int, int], mapping: typing.Union[str, None] = "identity"): + def __init__( + self, + reference: Reference, + point_in: FunctionInput, + direction_in: FunctionInput, + entity: typing.Tuple[int, int], + mapping: typing.Union[str, None] = "identity", + ): """Create the functional. Args: @@ -513,8 +541,14 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: class PointNormalDerivativeEvaluation(PointDirectionalDerivativeEvaluation): """A point evaluation of a normal derivative.""" - def __init__(self, reference: Reference, point_in: FunctionInput, edge: Reference, - entity: typing.Tuple[int, int], mapping: typing.Union[str, None] = "identity"): + def __init__( + self, + reference: Reference, + point_in: FunctionInput, + edge: Reference, + entity: typing.Tuple[int, int], + mapping: typing.Union[str, None] = "identity", + ): """Create the functional. Args: @@ -548,9 +582,14 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: class PointComponentSecondDerivativeEvaluation(BaseFunctional): """A point evaluation of a component of a second derivative.""" - def __init__(self, reference: Reference, point_in: FunctionInput, - component: typing.Tuple[int, int], - entity: typing.Tuple[int, int], mapping: typing.Union[str, None] = "identity"): + def __init__( + self, + reference: Reference, + point_in: FunctionInput, + component: typing.Tuple[int, int], + entity: typing.Tuple[int, int], + mapping: typing.Union[str, None] = "identity", + ): """Create the functional. Args: @@ -613,9 +652,15 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: class PointInnerProduct(BaseFunctional): """An evaluation of an inner product at a point.""" - def __init__(self, reference: Reference, point_in: FunctionInput, lvec: FunctionInput, - rvec: FunctionInput, - entity: typing.Tuple[int, int], mapping: typing.Union[str, None] = "identity"): + def __init__( + self, + reference: Reference, + point_in: FunctionInput, + lvec: FunctionInput, + rvec: FunctionInput, + entity: typing.Tuple[int, int], + mapping: typing.Union[str, None] = "identity", + ): """Create the functional. Args: @@ -701,8 +746,14 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: class DotPointEvaluation(BaseFunctional): """A point evaluation in a given direction.""" - def __init__(self, reference: Reference, point_in: FunctionInput, vector_in: FunctionInput, - entity: typing.Tuple[int, int], mapping: typing.Union[str, None] = "identity"): + def __init__( + self, + reference: Reference, + point_in: FunctionInput, + vector_in: FunctionInput, + entity: typing.Tuple[int, int], + mapping: typing.Union[str, None] = "identity", + ): """Create the functional. Args: @@ -782,8 +833,13 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: class PointDivergenceEvaluation(BaseFunctional): """A point evaluation of the divergence.""" - def __init__(self, reference: Reference, point_in: FunctionInput, - entity: typing.Tuple[int, int], mapping: typing.Union[str, None] = "identity"): + def __init__( + self, + reference: Reference, + point_in: FunctionInput, + entity: typing.Tuple[int, int], + mapping: typing.Union[str, None] = "identity", + ): """Create the functional. Args: @@ -860,8 +916,13 @@ class IntegralAgainst(BaseFunctional): f: AnyFunction - def __init__(self, reference: Reference, f_in: FunctionInput, - entity: typing.Tuple[int, int], mapping: typing.Union[str, None] = "identity"): + def __init__( + self, + reference: Reference, + f_in: FunctionInput, + entity: typing.Tuple[int, int], + mapping: typing.Union[str, None] = "identity", + ): """Create the functional. Args: @@ -879,14 +940,18 @@ def __init__(self, reference: Reference, f_in: FunctionInput, if f.is_vector: assert len(f) == self.integral_domain.tdim self.f = mappings.contravariant( - f, self.integral_domain.get_map_to_self(), - self.integral_domain.get_inverse_map_to_self()) + f, + self.integral_domain.get_map_to_self(), + self.integral_domain.get_inverse_map_to_self(), + ) elif f.is_matrix: assert f.shape[0] == self.integral_domain.tdim assert f.shape[1] == self.integral_domain.tdim self.f = mappings.double_contravariant( - f, self.integral_domain.get_map_to_self(), - self.integral_domain.get_inverse_map_to_self()) + f, + self.integral_domain.get_map_to_self(), + self.integral_domain.get_inverse_map_to_self(), + ) else: self.f = f @@ -953,8 +1018,13 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: class IntegralOfDivergenceAgainst(BaseFunctional): """An integral of the divergence against a function.""" - def __init__(self, reference: Reference, f_in: FunctionInput, - entity: typing.Tuple[int, int], mapping: typing.Union[str, None] = "identity"): + def __init__( + self, + reference: Reference, + f_in: FunctionInput, + entity: typing.Tuple[int, int], + mapping: typing.Union[str, None] = "identity", + ): """Create the functional. Args: @@ -1026,9 +1096,15 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: class IntegralOfDirectionalMultiderivative(BaseFunctional): """An integral of a directional derivative of a scalar function.""" - def __init__(self, reference: Reference, directions: SetOfPoints, - orders: typing.Tuple[int, ...], entity: typing.Tuple[int, int], scale: int = 1, - mapping: typing.Union[str, None] = "identity"): + def __init__( + self, + reference: Reference, + directions: SetOfPoints, + orders: typing.Tuple[int, ...], + entity: typing.Tuple[int, int], + scale: int = 1, + mapping: typing.Union[str, None] = "identity", + ): """Create the functional. Args: @@ -1122,9 +1198,15 @@ class IntegralMoment(BaseFunctional): f: AnyFunction - def __init__(self, reference: Reference, - f_in: FunctionInput, dof: BaseFunctional, entity: typing.Tuple[int, int], - mapping: typing.Union[str, None] = "identity", map_function: bool = True): + def __init__( + self, + reference: Reference, + f_in: FunctionInput, + dof: BaseFunctional, + entity: typing.Tuple[int, int], + mapping: typing.Union[str, None] = "identity", + map_function: bool = True, + ): """Create the functional. Args: @@ -1149,23 +1231,26 @@ def __init__(self, reference: Reference, if id_def.default_reference() != id_def: if self.f.is_vector and len(self.f) != reference.gdim: self.f = mappings.contravariant( - self.f, id_def.get_map_to_self(), - id_def.get_inverse_map_to_self()) + self.f, id_def.get_map_to_self(), id_def.get_inverse_map_to_self() + ) elif self.f.is_matrix: assert self.f.shape[0] == id_def.tdim assert self.f.shape[1] == id_def.tdim self.f = mappings.double_contravariant( - self.f, id_def.get_map_to_self(), - id_def.get_inverse_map_to_self()) + self.f, id_def.get_map_to_self(), id_def.get_inverse_map_to_self() + ) # Map from default reference to reference if map_function and reference != reference.default_reference(): if mapping is None or not hasattr(mappings, f"{mapping}_inverse_transpose"): raise NonDefaultReferenceError() mf = getattr(mappings, f"{mapping}_inverse_transpose") - self.f = mf(self.f, reference.get_map_to_self(), - reference.get_inverse_map_to_self(), - substitute=False) + self.f = mf( + self.f, + reference.get_map_to_self(), + reference.get_inverse_map_to_self(), + substitute=False, + ) self.f *= id_def.volume() / self.integral_domain.volume() def _eval_symbolic(self, function: AnyFunction) -> AnyFunction: @@ -1248,9 +1333,9 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: desc += "\\\\".join([_to_tex(i) for i in self.f]) desc += "\\end{array}\\right)" else: - if len(self.f) == self.integral_domain.tdim ** 2: + if len(self.f) == self.integral_domain.tdim**2: size = self.integral_domain.tdim - elif len(self.f) == self.reference.tdim ** 2: + elif len(self.f) == self.reference.tdim**2: size = self.reference.tdim else: raise NotImplementedError() @@ -1258,9 +1343,12 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: desc += f"\\displaystyle\\int_{{{entity}}}" desc += "\\mathbf{V}:" desc += "\\left(\\begin{array}{" + "c" * size + "}" - desc += "\\\\".join(["&".join( - [_to_tex(self.f[i]) for i in range(size * row, size * (row + 1))] - ) for row in range(size)]) + desc += "\\\\".join( + [ + "&".join([_to_tex(self.f[i]) for i in range(size * row, size * (row + 1))]) + for row in range(size) + ] + ) desc += "\\end{array}\\right)" else: desc = "v\\mapsto" @@ -1276,9 +1364,15 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: class DerivativeIntegralMoment(IntegralMoment): """An integral moment of the derivative of a scalar function.""" - def __init__(self, reference: Reference, f: FunctionInput, - dot_with_in: FunctionInput, dof: BaseFunctional, entity: typing.Tuple[int, int], - mapping: typing.Union[str, None] = "identity"): + def __init__( + self, + reference: Reference, + f: FunctionInput, + dot_with_in: FunctionInput, + dof: BaseFunctional, + entity: typing.Tuple[int, int], + mapping: typing.Union[str, None] = "identity", + ): """Create the functional. Args: @@ -1338,9 +1432,14 @@ def _eval_symbolic(self, function: AnyFunction) -> AnyFunction: class DivergenceIntegralMoment(IntegralMoment): """An integral moment of the divergence of a vector function.""" - def __init__(self, reference: Reference, f_in: FunctionInput, - dof: BaseFunctional, entity: typing.Tuple[int, int], - mapping: typing.Union[str, None] = "identity"): + def __init__( + self, + reference: Reference, + f_in: FunctionInput, + dof: BaseFunctional, + entity: typing.Tuple[int, int], + mapping: typing.Union[str, None] = "identity", + ): """Create the functional. Args: @@ -1392,9 +1491,14 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: class TangentIntegralMoment(IntegralMoment): """An integral moment in the tangential direction.""" - def __init__(self, reference: Reference, f_in: FunctionInput, - dof: BaseFunctional, entity: typing.Tuple[int, int], - mapping: typing.Union[str, None] = "covariant"): + def __init__( + self, + reference: Reference, + f_in: FunctionInput, + dof: BaseFunctional, + entity: typing.Tuple[int, int], + mapping: typing.Union[str, None] = "covariant", + ): """Create the functional. Args: @@ -1408,8 +1512,13 @@ def __init__(self, reference: Reference, f_in: FunctionInput, integral_domain = reference.sub_entity(*entity) assert self._scalar_f.is_scalar super().__init__( - reference, tuple(self._scalar_f * i for i in integral_domain.tangent()), dof, - entity=entity, mapping=mapping, map_function=False) + reference, + tuple(self._scalar_f * i for i in integral_domain.tangent()), + dof, + entity=entity, + mapping=mapping, + map_function=False, + ) def dof_direction(self) -> typing.Union[PointType, None]: """Get the direction of the DOF. @@ -1436,7 +1545,7 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: desc += "\\hat{\\boldsymbol{t}}" + f"_{{{entity_n}}}" return desc, [ entity_def, - f"\\(\\hat{{\\boldsymbol{{t}}}}_{{{entity_n}}}\\) is the tangent to edge {entity_n}" + f"\\(\\hat{{\\boldsymbol{{t}}}}_{{{entity_n}}}\\) is the tangent to edge {entity_n}", ] name = "Tangential integral moment" @@ -1445,9 +1554,14 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: class NormalIntegralMoment(IntegralMoment): """An integral moment in the normal direction.""" - def __init__(self, reference: Reference, f_in: FunctionInput, - dof: BaseFunctional, entity: typing.Tuple[int, int], - mapping: typing.Union[str, None] = "contravariant"): + def __init__( + self, + reference: Reference, + f_in: FunctionInput, + dof: BaseFunctional, + entity: typing.Tuple[int, int], + mapping: typing.Union[str, None] = "contravariant", + ): """Create the functional. Args: @@ -1461,8 +1575,13 @@ def __init__(self, reference: Reference, f_in: FunctionInput, assert self._scalar_f.is_scalar integral_domain = reference.sub_entity(*entity) super().__init__( - reference, tuple(self._scalar_f * i for i in integral_domain.normal()), dof, - entity=entity, mapping=mapping, map_function=False) + reference, + tuple(self._scalar_f * i for i in integral_domain.normal()), + dof, + entity=entity, + mapping=mapping, + map_function=False, + ) def dof_direction(self) -> typing.Union[PointType, None]: """Get the direction of the DOF. @@ -1489,7 +1608,7 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: desc += "\\hat{\\boldsymbol{n}}" + f"_{{{entity_n}}}" return desc, [ entity_def, - f"\\(\\hat{{\\boldsymbol{{n}}}}_{{{entity_n}}}\\) is the normal to facet {entity_n}" + f"\\(\\hat{{\\boldsymbol{{n}}}}_{{{entity_n}}}\\) is the normal to facet {entity_n}", ] name = "Normal integral moment" @@ -1498,9 +1617,14 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: class NormalDerivativeIntegralMoment(DerivativeIntegralMoment): """An integral moment in the normal direction.""" - def __init__(self, reference: Reference, f_in: FunctionInput, - dof: BaseFunctional, entity: typing.Tuple[int, int], - mapping: typing.Union[str, None] = "identity"): + def __init__( + self, + reference: Reference, + f_in: FunctionInput, + dof: BaseFunctional, + entity: typing.Tuple[int, int], + mapping: typing.Union[str, None] = "identity", + ): """Create the functional. Args: @@ -1513,8 +1637,9 @@ def __init__(self, reference: Reference, f_in: FunctionInput, f = parse_function_input(f_in) assert f.is_scalar integral_domain = reference.sub_entity(*entity) - super().__init__(reference, f, integral_domain.normal(), dof, - entity=entity, mapping=mapping) + super().__init__( + reference, f, integral_domain.normal(), dof, entity=entity, mapping=mapping + ) def get_tex(self) -> typing.Tuple[str, typing.List[str]]: """Get a representation of the functional as TeX, and list of terms involved. @@ -1533,7 +1658,7 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: desc += "{\\partial\\hat{\\boldsymbol{n}}" + f"_{{{entity_n}}}" + "}" return desc, [ entity_def, - f"\\(\\hat{{\\boldsymbol{{n}}}}_{{{entity_n}}}\\) is the normal to facet {entity_n}" + f"\\(\\hat{{\\boldsymbol{{n}}}}_{{{entity_n}}}\\) is the normal to facet {entity_n}", ] name = "Normal derivative integral moment" @@ -1542,10 +1667,16 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: class InnerProductIntegralMoment(IntegralMoment): """An integral moment of the inner product with a vector.""" - def __init__(self, reference: Reference, f_in: FunctionInput, - inner_with_left_in: FunctionInput, inner_with_right_in: FunctionInput, - dof: BaseFunctional, entity: typing.Tuple[int, int], - mapping: typing.Union[str, None] = "identity"): + def __init__( + self, + reference: Reference, + f_in: FunctionInput, + inner_with_left_in: FunctionInput, + inner_with_right_in: FunctionInput, + dof: BaseFunctional, + entity: typing.Tuple[int, int], + mapping: typing.Union[str, None] = "identity", + ): """Create the functional. Args: @@ -1578,8 +1709,11 @@ def dot(self, function: AnyFunction) -> ScalarFunction: The inner product of the function and the moment direction """ assert function.is_matrix - return self.inner_with_left.dot( - function @ self.inner_with_right) * self.f * self.integral_domain.jacobian() + return ( + self.inner_with_left.dot(function @ self.inner_with_right) + * self.f + * self.integral_domain.jacobian() + ) def dof_direction(self) -> typing.Union[PointType, None]: """Get the direction of the DOF. @@ -1616,9 +1750,14 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: class NormalInnerProductIntegralMoment(InnerProductIntegralMoment): """An integral moment of the inner product with the normal direction.""" - def __init__(self, reference: Reference, f_in: FunctionInput, - dof: BaseFunctional, entity: typing.Tuple[int, int], - mapping: typing.Union[str, None] = "double_contravariant"): + def __init__( + self, + reference: Reference, + f_in: FunctionInput, + dof: BaseFunctional, + entity: typing.Tuple[int, int], + mapping: typing.Union[str, None] = "double_contravariant", + ): """Create the functional. Args: @@ -1631,8 +1770,15 @@ def __init__(self, reference: Reference, f_in: FunctionInput, f = parse_function_input(f_in) assert f.is_scalar integral_domain = reference.sub_entity(*entity) - super().__init__(reference, f, integral_domain.normal(), - integral_domain.normal(), dof, entity=entity, mapping=mapping) + super().__init__( + reference, + f, + integral_domain.normal(), + integral_domain.normal(), + dof, + entity=entity, + mapping=mapping, + ) def get_tex(self) -> typing.Tuple[str, typing.List[str]]: """Get a representation of the functional as TeX, and list of terms involved. @@ -1653,7 +1799,7 @@ def get_tex(self) -> typing.Tuple[str, typing.List[str]]: desc += "\\hat{\\boldsymbol{n}}" + f"_{{{entity_n}}}" return desc, [ entity_def, - f"\\(\\hat{{\\boldsymbol{{n}}}}_{{{entity_n}}}\\) is the normal to facet {entity_n}" + f"\\(\\hat{{\\boldsymbol{{n}}}}_{{{entity_n}}}\\) is the normal to facet {entity_n}", ] name = "Normal inner product integral moment" diff --git a/symfem/functions.py b/symfem/functions.py index 30d33717..15f0eea7 100644 --- a/symfem/functions.py +++ b/symfem/functions.py @@ -9,22 +9,21 @@ import symfem.references -from .geometry import PointType -from .symbols import AxisVariables, AxisVariablesNotSingle, t, x +from symfem.geometry import PointType +from symfem.symbols import AxisVariables, AxisVariablesNotSingle, t, x SingleSympyFormat = typing.Union[ sympy.core.expr.Expr, typing.Tuple[sympy.core.expr.Expr, ...], - sympy.matrices.dense.MutableDenseMatrix + sympy.matrices.dense.MutableDenseMatrix, ] SympyFormat = typing.Union[ SingleSympyFormat, - typing.Dict[typing.Tuple[typing.Tuple[sympy.core.expr.Expr, ...], ...], SingleSympyFormat] + typing.Dict[typing.Tuple[typing.Tuple[sympy.core.expr.Expr, ...], ...], SingleSympyFormat], ] _ValuesToSubstitute = typing.Union[ - typing.Tuple[typing.Any, ...], - typing.List[typing.Any], - typing.Any] + typing.Tuple[typing.Any, ...], typing.List[typing.Any], typing.Any +] def _to_sympy_format(item: typing.Any) -> SympyFormat: @@ -81,9 +80,7 @@ def _check_equal(first: SympyFormat, second: SympyFormat) -> bool: return False return True - if isinstance( - first, sympy.matrices.dense.MutableDenseMatrix - ) and isinstance( + if isinstance(first, sympy.matrices.dense.MutableDenseMatrix) and isinstance( second, sympy.matrices.dense.MutableDenseMatrix ): if first.rows != second.rows: @@ -291,8 +288,10 @@ def norm(self): @abstractmethod def integral( - self, domain: symfem.references.Reference, vars: AxisVariablesNotSingle = x, - dummy_vars: AxisVariablesNotSingle = t + self, + domain: symfem.references.Reference, + vars: AxisVariablesNotSingle = x, + dummy_vars: AxisVariablesNotSingle = t, ) -> ScalarFunction: """Compute the integral of the function. @@ -331,11 +330,14 @@ def __iter__(self) -> typing.Iterator[AnyFunction]: """Iterate through components of vector function.""" raise TypeError(f"'{self.__class__.__name__}' object is not iterable") - def integrate(self, *limits: typing.Tuple[ - sympy.core.symbol.Symbol, - typing.Union[int, sympy.core.expr.Expr], - typing.Union[int, sympy.core.expr.Expr] - ]): + def integrate( + self, + *limits: typing.Tuple[ + sympy.core.symbol.Symbol, + typing.Union[int, sympy.core.expr.Expr], + typing.Union[int, sympy.core.expr.Expr], + ], + ): """Integrate the function. Args: @@ -372,12 +374,16 @@ def shape(self) -> typing.Tuple[int, ...]: raise AttributeError(f"'{self.__class__.__name__}' object has no attribute 'shape'") def plot( - self, reference: symfem.references.Reference, filename: typing.Union[str, typing.List[str]], + self, + reference: symfem.references.Reference, + filename: typing.Union[str, typing.List[str]], dof_point: typing.Optional[PointType] = None, dof_direction: typing.Optional[PointType] = None, dof_entity: typing.Optional[typing.Tuple[int, int]] = None, - dof_n: typing.Optional[int] = None, value_scale: sympy.core.expr.Expr = sympy.Integer(1), - plot_options: typing.Dict[str, typing.Any] = {}, **kwargs: typing.Any + dof_n: typing.Optional[int] = None, + value_scale: sympy.core.expr.Expr = sympy.Integer(1), + plot_options: typing.Dict[str, typing.Any] = {}, + **kwargs: typing.Any, ): """Plot the function. @@ -392,11 +398,11 @@ def plot( plot_options: Options for the plot kwargs: Keyword arguments """ - from .plotting import Picture, colors + from symfem.plotting import Picture, colors extra: typing.Tuple[int, ...] = tuple() if self.is_scalar: - extra = (0, ) + extra = (0,) img = Picture(**kwargs) @@ -417,7 +423,9 @@ def plot( c = colors.BLUE img.add_line( reference.vertices[reference.edges[entity][0]] + extra, - reference.vertices[reference.edges[entity][1]] + extra, c) + reference.vertices[reference.edges[entity][1]] + extra, + c, + ) for dim, entity in ze: if dim == reference.tdim: @@ -426,8 +434,9 @@ def plot( if (dim, entity) in ze: if dof_direction is not None: assert dof_point is not None and dof_n is not None - img.add_dof_arrow(dof_point + extra, dof_direction + extra, dof_n, - colors.PURPLE, bold=False) + img.add_dof_arrow( + dof_point + extra, dof_direction + extra, dof_n, colors.PURPLE, bold=False + ) elif dof_point is not None: assert dof_n is not None img.add_dof_marker(dof_point + extra, dof_n, colors.PURPLE, bold=False) @@ -435,8 +444,11 @@ def plot( img.save(filename, plot_options=plot_options) def plot_values( - self, reference: symfem.references.Reference, img: typing.Any, - value_scale: sympy.core.expr.Expr = sympy.Integer(1), n: int = 6 + self, + reference: symfem.references.Reference, + img: typing.Any, + value_scale: sympy.core.expr.Expr = sympy.Integer(1), + n: int = 6, ): """Plot the function's values. @@ -517,8 +529,10 @@ def __init__(self, f: typing.Union[int, sympy.core.expr.Expr]): else: self._f = f assert isinstance(self._f, sympy.core.expr.Expr) - self._plot_beziers: typing.Dict[typing.Tuple[symfem.references.Reference, int], typing.List[ - typing.Tuple[PointType, PointType, PointType, PointType]]] = {} + self._plot_beziers: typing.Dict[ + typing.Tuple[symfem.references.Reference, int], + typing.List[typing.Tuple[PointType, PointType, PointType, PointType]], + ] = {} def __add__(self, other: typing.Any) -> ScalarFunction: """Add.""" @@ -594,7 +608,7 @@ def __rmatmul__(self, other: typing.Any): def __pow__(self, other: typing.Any) -> ScalarFunction: """Raise to a power.""" - return ScalarFunction(self._f ** other) + return ScalarFunction(self._f**other) def __neg__(self) -> ScalarFunction: """Negate.""" @@ -689,9 +703,9 @@ def jacobian(self, dim: int) -> MatrixFunction: Returns: The jacobian """ - return MatrixFunction(tuple( - tuple(self.jacobian_component((i, j)) for j in range(dim)) - for i in range(dim))) + return MatrixFunction( + tuple(tuple(self.jacobian_component((i, j)) for j in range(dim)) for i in range(dim)) + ) def dot(self, other_in: FunctionInput) -> ScalarFunction: """Compute the dot product with another function. @@ -755,8 +769,10 @@ def norm(self) -> ScalarFunction: return ScalarFunction(abs(self._f)) def integral( - self, domain: symfem.references.Reference, vars: AxisVariablesNotSingle = x, - dummy_vars: AxisVariablesNotSingle = t + self, + domain: symfem.references.Reference, + vars: AxisVariablesNotSingle = x, + dummy_vars: AxisVariablesNotSingle = t, ) -> ScalarFunction: """Compute the integral of the function. @@ -785,11 +801,14 @@ def integral( out *= domain.jacobian() return ScalarFunction(out.integrate(*limits)) - def integrate(self, *limits: typing.Tuple[ - sympy.core.symbol.Symbol, - typing.Union[int, sympy.core.expr.Expr], - typing.Union[int, sympy.core.expr.Expr] - ]): + def integrate( + self, + *limits: typing.Tuple[ + sympy.core.symbol.Symbol, + typing.Union[int, sympy.core.expr.Expr], + typing.Union[int, sympy.core.expr.Expr], + ], + ): """Integrate the function. Args: @@ -801,8 +820,11 @@ def integrate(self, *limits: typing.Tuple[ return ScalarFunction(self._f.integrate(*limits)) def plot_values( - self, reference: symfem.references.Reference, img: typing.Any, - value_scale: sympy.core.expr.Expr = sympy.Integer(1), n: int = 6 + self, + reference: symfem.references.Reference, + img: typing.Any, + value_scale: sympy.core.expr.Expr = sympy.Integer(1), + n: int = 6, ): """Plot the function's values. @@ -812,7 +834,8 @@ def plot_values( value_scale: The scale factor for the function values n: The number of points per side for plotting """ - from .plotting import Picture, colors + from symfem.plotting import Picture, colors + assert isinstance(img, Picture) if (reference, n) not in self._plot_beziers: @@ -840,9 +863,14 @@ def plot_values( dj = deriv.subs(x, pj).dot(d_pj - pts[j]).as_sympy() assert isinstance(di, sympy.core.expr.Expr) assert isinstance(dj, sympy.core.expr.Expr) - self._plot_beziers[(reference, n)].append(( - tuple(pi) + (evals[i], ), tuple(d_pi) + (evals[i] + di * value_scale, ), - tuple(d_pj) + (evals[j] + dj * value_scale, ), tuple(pj) + (evals[j], ))) + self._plot_beziers[(reference, n)].append( + ( + tuple(pi) + (evals[i],), + tuple(d_pi) + (evals[i] + di * value_scale,), + tuple(d_pj) + (evals[j] + dj * value_scale,), + tuple(pj) + (evals[j],), + ) + ) for s, m1, m2, e in self._plot_beziers[(reference, n)]: img.add_bezier(s, m1, m2, e, colors.ORANGE) @@ -895,16 +923,20 @@ class VectorFunction(AnyFunction): _vec: tuple[ScalarFunction, ...] - def __init__(self, vec: typing.Union[ - typing.Tuple[typing.Union[AnyFunction, int, sympy.core.expr.Expr], ...], - typing.List[typing.Union[AnyFunction, int, sympy.core.expr.Expr]] - ]): + def __init__( + self, + vec: typing.Union[ + typing.Tuple[typing.Union[AnyFunction, int, sympy.core.expr.Expr], ...], + typing.List[typing.Union[AnyFunction, int, sympy.core.expr.Expr]], + ], + ): """Create a vector-valued function. Args: vec: The sympy representation of the function. """ - from .basis_functions import BasisFunction + from symfem.basis_functions import BasisFunction + super().__init__(vector=True) vec_l = [] for i in vec: @@ -919,8 +951,12 @@ def __init__(self, vec: typing.Union[ for i in self._vec: assert i.is_scalar - self._plot_arrows: typing.Dict[typing.Tuple[symfem.references.Reference, int], typing.List[ - typing.Tuple[typing.Tuple[sympy.core.expr.Expr, ...], VectorFunction, float]]] = {} + self._plot_arrows: typing.Dict[ + typing.Tuple[symfem.references.Reference, int], + typing.List[ + typing.Tuple[typing.Tuple[sympy.core.expr.Expr, ...], VectorFunction, float] + ], + ] = {} def __len__(self): """Get the length of the vector.""" @@ -933,7 +969,7 @@ def shape(self) -> typing.Tuple[int, ...]: Returns: The value shape """ - return (len(self), ) + return (len(self),) def __getitem__(self, key) -> typing.Union[ScalarFunction, VectorFunction]: """Get a component or slice of the function.""" @@ -1027,9 +1063,9 @@ def __rmatmul__(self, other: typing.Any) -> VectorFunction: def __pow__(self, other: typing.Any) -> VectorFunction: """Raise to a power.""" if isinstance(other, ScalarFunction): - return VectorFunction(tuple(i._f ** other._f for i in self._vec)) + return VectorFunction(tuple(i._f**other._f for i in self._vec)) if isinstance(other, (int, sympy.core.expr.Expr)): - return VectorFunction(tuple(i._f ** other for i in self._vec)) + return VectorFunction(tuple(i._f**other for i in self._vec)) return NotImplemented def as_sympy(self) -> SympyFormat: @@ -1046,9 +1082,11 @@ def as_tex(self) -> str: Returns: A TeX string """ - return "\\left(\\begin{array}{c}" + "\\\\".join([ - "\\displaystyle " + i.as_tex() for i in self._vec - ]) + "\\end{array}\\right)" + return ( + "\\left(\\begin{array}{c}" + + "\\\\".join(["\\displaystyle " + i.as_tex() for i in self._vec]) + + "\\end{array}\\right)" + ) def subs(self, vars: AxisVariables, values: ValuesToSubstitute) -> VectorFunction: """Substitute values into the function. @@ -1144,9 +1182,13 @@ def cross(self, other_in: FunctionInput) -> typing.Union[VectorFunction, ScalarF return self[0] * other[1] - self[1] * other[0] else: assert len(self) == 3 - return VectorFunction([self[1] * other[2] - self[2] * other[1], - self[2] * other[0] - self[0] * other[2], - self[0] * other[1] - self[1] * other[0]]) + return VectorFunction( + [ + self[1] * other[2] - self[2] * other[1], + self[2] * other[0] - self[0] * other[2], + self[0] * other[1] - self[1] * other[0], + ] + ) def div(self) -> ScalarFunction: """Compute the div of the function. @@ -1174,11 +1216,13 @@ def curl(self) -> VectorFunction: The curl """ assert len(self._vec) == 3 - return VectorFunction([ - self._vec[2].diff(x[1]) - self._vec[1].diff(x[2]), - self._vec[0].diff(x[2]) - self._vec[2].diff(x[0]), - self._vec[1].diff(x[0]) - self._vec[0].diff(x[1]) - ]) + return VectorFunction( + [ + self._vec[2].diff(x[1]) - self._vec[1].diff(x[2]), + self._vec[0].diff(x[2]) - self._vec[2].diff(x[0]), + self._vec[1].diff(x[0]) - self._vec[0].diff(x[1]), + ] + ) def norm(self) -> ScalarFunction: """Compute the norm of the function. @@ -1188,12 +1232,14 @@ def norm(self) -> ScalarFunction: """ a = sympy.Integer(0) for i in self._vec: - a += i._f ** 2 + a += i._f**2 return ScalarFunction(sympy.sqrt(a)) def integral( - self, domain: symfem.references.Reference, vars: AxisVariablesNotSingle = x, - dummy_vars: AxisVariablesNotSingle = t + self, + domain: symfem.references.Reference, + vars: AxisVariablesNotSingle = x, + dummy_vars: AxisVariablesNotSingle = t, ) -> ScalarFunction: """Compute the integral of the function. @@ -1221,8 +1267,11 @@ def __next__(self): raise StopIteration def plot_values( - self, reference: symfem.references.Reference, img: typing.Any, - value_scale: sympy.core.expr.Expr = sympy.Integer(1), n: int = 6 + self, + reference: symfem.references.Reference, + img: typing.Any, + value_scale: sympy.core.expr.Expr = sympy.Integer(1), + n: int = 6, ): """Plot the function's values. @@ -1232,7 +1281,8 @@ def plot_values( value_scale: The scale factor for the function values n: The number of points per side for plotting """ - from .plotting import Picture, colors + from symfem.plotting import Picture, colors + assert isinstance(img, Picture) if (reference, n) not in self._plot_arrows: @@ -1278,20 +1328,25 @@ class MatrixFunction(AnyFunction): _mat: typing.Tuple[typing.Tuple[ScalarFunction, ...], ...] - def __init__(self, mat: typing.Union[ - typing.Tuple[typing.Tuple[typing.Union[AnyFunction, int, sympy.core.expr.Expr], - ...], ...], - typing.Tuple[typing.List[typing.Union[AnyFunction, int, sympy.core.expr.Expr]], ...], - typing.List[typing.Tuple[typing.Union[AnyFunction, int, sympy.core.expr.Expr], ...]], - typing.List[typing.List[typing.Union[AnyFunction, int, sympy.core.expr.Expr]]], - sympy.matrices.dense.MutableDenseMatrix - ]): + def __init__( + self, + mat: typing.Union[ + typing.Tuple[ + typing.Tuple[typing.Union[AnyFunction, int, sympy.core.expr.Expr], ...], ... + ], + typing.Tuple[typing.List[typing.Union[AnyFunction, int, sympy.core.expr.Expr]], ...], + typing.List[typing.Tuple[typing.Union[AnyFunction, int, sympy.core.expr.Expr], ...]], + typing.List[typing.List[typing.Union[AnyFunction, int, sympy.core.expr.Expr]]], + sympy.matrices.dense.MutableDenseMatrix, + ], + ): """Create a matrix-valued function. Args: mat: The sympy representation of the function. """ - from .basis_functions import BasisFunction + from symfem.basis_functions import BasisFunction + super().__init__(matrix=True) if isinstance(mat, sympy.matrices.dense.MutableDenseMatrix): mat = tuple(tuple(mat[i, j] for j in range(mat.cols)) for i in range(mat.rows)) @@ -1358,8 +1413,12 @@ def __add__(self, other: typing.Any) -> MatrixFunction: """Add.""" if isinstance(other, MatrixFunction): assert self.shape == other.shape - return MatrixFunction(tuple(tuple( - ii._f + jj._f for ii, jj in zip(i, j)) for i, j in zip(self._mat, other._mat))) + return MatrixFunction( + tuple( + tuple(ii._f + jj._f for ii, jj in zip(i, j)) + for i, j in zip(self._mat, other._mat) + ) + ) if isinstance(other, (list, tuple)): data = [] for i, j in zip(self._mat, other): @@ -1369,16 +1428,24 @@ def __add__(self, other: typing.Any) -> MatrixFunction: if isinstance(other, sympy.matrices.dense.MutableDenseMatrix): assert other.rows == self.shape[0] assert other.cols == self.shape[1] - return MatrixFunction([[self._mat[i][j]._f + other[i, j] - for j in range(self.shape[1])] for i in range(self.shape[0])]) + return MatrixFunction( + [ + [self._mat[i][j]._f + other[i, j] for j in range(self.shape[1])] + for i in range(self.shape[0]) + ] + ) return NotImplemented def __radd__(self, other: typing.Any) -> MatrixFunction: """Add.""" if isinstance(other, MatrixFunction): assert self.shape == other.shape - return MatrixFunction(tuple(tuple( - ii._f + jj._f for ii, jj in zip(i, j)) for i, j in zip(other._mat, self._mat))) + return MatrixFunction( + tuple( + tuple(ii._f + jj._f for ii, jj in zip(i, j)) + for i, j in zip(other._mat, self._mat) + ) + ) if isinstance(other, (list, tuple)): data = [] for i, j in zip(other, self._mat): @@ -1388,16 +1455,24 @@ def __radd__(self, other: typing.Any) -> MatrixFunction: if isinstance(other, sympy.matrices.dense.MutableDenseMatrix): assert other.rows == self.shape[0] assert other.cols == self.shape[1] - return MatrixFunction([[other[i, j] + self._mat[i][j]._f - for j in range(self.shape[1])] for i in range(self.shape[0])]) + return MatrixFunction( + [ + [other[i, j] + self._mat[i][j]._f for j in range(self.shape[1])] + for i in range(self.shape[0]) + ] + ) return NotImplemented def __sub__(self, other: typing.Any) -> MatrixFunction: """Subtract.""" if isinstance(other, MatrixFunction): assert self.shape == other.shape - return MatrixFunction(tuple(tuple( - ii._f - jj._f for ii, jj in zip(i, j)) for i, j in zip(self._mat, other._mat))) + return MatrixFunction( + tuple( + tuple(ii._f - jj._f for ii, jj in zip(i, j)) + for i, j in zip(self._mat, other._mat) + ) + ) if isinstance(other, (list, tuple)): data = [] for i, j in zip(self._mat, other): @@ -1407,16 +1482,24 @@ def __sub__(self, other: typing.Any) -> MatrixFunction: if isinstance(other, sympy.matrices.dense.MutableDenseMatrix): assert other.rows == self.shape[0] assert other.cols == self.shape[1] - return MatrixFunction([[self._mat[i][j]._f - other[i, j] - for j in range(self.shape[1])] for i in range(self.shape[0])]) + return MatrixFunction( + [ + [self._mat[i][j]._f - other[i, j] for j in range(self.shape[1])] + for i in range(self.shape[0]) + ] + ) return NotImplemented def __rsub__(self, other: typing.Any) -> MatrixFunction: """Subtract.""" if isinstance(other, MatrixFunction): assert self.shape == other.shape - return MatrixFunction(tuple(tuple( - ii._f - jj._f for ii, jj in zip(i, j)) for i, j in zip(other._mat, self._mat))) + return MatrixFunction( + tuple( + tuple(ii._f - jj._f for ii, jj in zip(i, j)) + for i, j in zip(other._mat, self._mat) + ) + ) if isinstance(other, (list, tuple)): data = [] for i, j in zip(other, self._mat): @@ -1426,8 +1509,12 @@ def __rsub__(self, other: typing.Any) -> MatrixFunction: if isinstance(other, sympy.matrices.dense.MutableDenseMatrix): assert other.rows == self.shape[0] assert other.cols == self.shape[1] - return MatrixFunction([[other[i, j] - self._mat[i][j]._f - for j in range(self.shape[1])] for i in range(self.shape[0])]) + return MatrixFunction( + [ + [other[i, j] - self._mat[i][j]._f for j in range(self.shape[1])] + for i in range(self.shape[0]) + ] + ) return NotImplemented def __neg__(self) -> MatrixFunction: @@ -1466,26 +1553,32 @@ def __matmul__(self, other: typing.Any) -> MatrixFunction: """Multiply.""" if isinstance(other, MatrixFunction): assert other.shape[0] == self.shape[1] - return MatrixFunction(tuple( - tuple(self.row(i).dot(other.col(j)) for j in range(other.shape[1])) - for i in range(self.shape[0]))) + return MatrixFunction( + tuple( + tuple(self.row(i).dot(other.col(j)) for j in range(other.shape[1])) + for i in range(self.shape[0]) + ) + ) return NotImplemented def __rmatmul__(self, other: typing.Any) -> MatrixFunction: """Multiply.""" if isinstance(other, MatrixFunction): assert self.shape[0] == other.shape[1] - return MatrixFunction(tuple( - tuple(other.row(i).dot(self.col(j)) for j in range(self.shape[1])) - for i in range(other.shape[0]))) + return MatrixFunction( + tuple( + tuple(other.row(i).dot(self.col(j)) for j in range(self.shape[1])) + for i in range(other.shape[0]) + ) + ) return NotImplemented def __pow__(self, other: typing.Any) -> MatrixFunction: """Raise to a power.""" if isinstance(other, ScalarFunction): - return MatrixFunction(tuple(tuple(j._f ** other._f for j in i) for i in self._mat)) + return MatrixFunction(tuple(tuple(j._f**other._f for j in i) for i in self._mat)) if isinstance(other, (int, sympy.core.expr.Expr)): - return MatrixFunction(tuple(tuple(j._f ** other for j in i) for i in self._mat)) + return MatrixFunction(tuple(tuple(j._f**other for j in i) for i in self._mat)) return NotImplemented def as_sympy(self) -> SympyFormat: @@ -1505,10 +1598,9 @@ def as_tex(self) -> str: out = "\\left(\\begin{array}{" out += "c" * self.shape[1] out += "}" - out += "\\\\".join([ - "&".join(["\\displaystyle " + j.as_tex() for j in i]) - for i in self._mat - ]) + out += "\\\\".join( + ["&".join(["\\displaystyle " + j.as_tex() for j in i]) for i in self._mat] + ) out += "\\end{array}\\right)" return out @@ -1534,9 +1626,12 @@ def diff(self, variable: sympy.core.symbol.Symbol) -> MatrixFunction: Returns: The differentiated function """ - return MatrixFunction(tuple( - tuple(self._mat[i][j].diff(variable) for j in range(self.shape[1])) - for i in range(self.shape[0]))) + return MatrixFunction( + tuple( + tuple(self._mat[i][j].diff(variable) for j in range(self.shape[1])) + for i in range(self.shape[0]) + ) + ) def directional_derivative(self, direction: PointType): """Compute a directional derivative. @@ -1638,8 +1733,10 @@ def norm(self) -> ScalarFunction: raise NotImplementedError() def integral( - self, domain: symfem.references.Reference, vars: AxisVariablesNotSingle = x, - dummy_vars: AxisVariablesNotSingle = t + self, + domain: symfem.references.Reference, + vars: AxisVariablesNotSingle = x, + dummy_vars: AxisVariablesNotSingle = t, ) -> ScalarFunction: """Compute the integral of the function. @@ -1702,14 +1799,16 @@ def maximum_degree(self, cell: symfem.references.Reference) -> int: FunctionInput = typing.Union[ AnyFunction, - sympy.core.expr.Expr, int, + sympy.core.expr.Expr, + int, typing.Tuple[typing.Union[sympy.core.expr.Expr, int, AnyFunction], ...], typing.List[typing.Union[sympy.core.expr.Expr, int, AnyFunction]], typing.Tuple[typing.Tuple[typing.Union[sympy.core.expr.Expr, int, AnyFunction], ...], ...], typing.Tuple[typing.List[typing.Union[sympy.core.expr.Expr, int, AnyFunction]], ...], typing.List[typing.Tuple[typing.Union[sympy.core.expr.Expr, int, AnyFunction], ...]], typing.List[typing.List[typing.Union[sympy.core.expr.Expr, int, AnyFunction]]], - sympy.matrices.dense.MutableDenseMatrix] + sympy.matrices.dense.MutableDenseMatrix, +] def parse_function_input(f: FunctionInput) -> AnyFunction: @@ -1742,7 +1841,7 @@ def parse_function_input(f: FunctionInput) -> AnyFunction: def parse_function_list_input( - functions: typing.Union[typing.List[FunctionInput], typing.Tuple[FunctionInput, ...]] + functions: typing.Union[typing.List[FunctionInput], typing.Tuple[FunctionInput, ...]], ) -> typing.List[AnyFunction]: """Parse a list of functions. diff --git a/symfem/geometry.py b/symfem/geometry.py index 33cb958e..fd4a64cf 100644 --- a/symfem/geometry.py +++ b/symfem/geometry.py @@ -9,10 +9,9 @@ PointTypeInput = typing.Union[ typing.Tuple[typing.Union[sympy.core.expr.Expr, int], ...], typing.List[typing.Union[sympy.core.expr.Expr, int]], - sympy.matrices.dense.MutableDenseMatrix] -SetOfPointsInput = typing.Union[ - typing.Tuple[PointTypeInput, ...], - typing.List[PointTypeInput]] + sympy.matrices.dense.MutableDenseMatrix, +] +SetOfPointsInput = typing.Union[typing.Tuple[PointTypeInput, ...], typing.List[PointTypeInput]] def _is_close(a: sympy.core.expr.Expr, b: int) -> bool: @@ -126,7 +125,7 @@ def point_in_triangle(point: PointType, triangle: SetOfPoints) -> bool: dot11 = _vdot(v1, v1) dot12 = _vdot(v1, v2) - det = (dot00 * dot11 - dot01 * dot01) + det = dot00 * dot11 - dot01 * dot01 u = (dot11 * dot02 - dot01 * dot12) / det v = (dot00 * dot12 - dot01 * dot02) / det diff --git a/symfem/mappings.py b/symfem/mappings.py index 4117e0ce..0582dc58 100644 --- a/symfem/mappings.py +++ b/symfem/mappings.py @@ -16,7 +16,9 @@ class MappingNotImplemented(NotImplementedError): def identity( - f_in: FunctionInput, map: PointType, inverse_map: PointType, + f_in: FunctionInput, + map: PointType, + inverse_map: PointType, substitute: bool = True, ) -> AnyFunction: """Map functions. @@ -37,7 +39,9 @@ def identity( def l2( - f_in: FunctionInput, map: PointType, inverse_map: PointType, + f_in: FunctionInput, + map: PointType, + inverse_map: PointType, substitute: bool = True, ) -> AnyFunction: """Map functions, scaling by the determinant of the jacobian. @@ -61,7 +65,9 @@ def l2( def covariant( - f_in: FunctionInput, map: PointType, inverse_map: PointType, + f_in: FunctionInput, + map: PointType, + inverse_map: PointType, substitute: bool = True, ) -> AnyFunction: """Map H(curl) functions. @@ -87,8 +93,7 @@ def covariant( def contravariant( - f_in: FunctionInput, map: PointType, inverse_map: PointType, - substitute: bool = True + f_in: FunctionInput, map: PointType, inverse_map: PointType, substitute: bool = True ) -> AnyFunction: """Map H(div) functions. @@ -114,7 +119,9 @@ def contravariant( def double_covariant( - f_in: FunctionInput, map: PointType, inverse_map: PointType, + f_in: FunctionInput, + map: PointType, + inverse_map: PointType, substitute: bool = True, ) -> MatrixFunction: """Map matrix functions. @@ -139,7 +146,9 @@ def double_covariant( def double_contravariant( - f_in: FunctionInput, map: PointType, inverse_map: PointType, + f_in: FunctionInput, + map: PointType, + inverse_map: PointType, substitute: bool = True, ) -> MatrixFunction: """Map matrix functions. @@ -165,7 +174,9 @@ def double_contravariant( def identity_inverse_transpose( - f_in: FunctionInput, map: PointType, inverse_map: PointType, + f_in: FunctionInput, + map: PointType, + inverse_map: PointType, substitute: bool = True, ) -> AnyFunction: """Inverse transpose of identity(). @@ -186,7 +197,9 @@ def identity_inverse_transpose( def l2_inverse_transpose( - f_in: FunctionInput, map: PointType, inverse_map: PointType, + f_in: FunctionInput, + map: PointType, + inverse_map: PointType, substitute: bool = True, ) -> AnyFunction: """Inverse transpose of l2(). @@ -210,7 +223,9 @@ def l2_inverse_transpose( def covariant_inverse_transpose( - f_in: FunctionInput, map: PointType, inverse_map: PointType, + f_in: FunctionInput, + map: PointType, + inverse_map: PointType, substitute: bool = True, ) -> AnyFunction: """Inverse transpose of covariant(). @@ -236,7 +251,9 @@ def covariant_inverse_transpose( def contravariant_inverse_transpose( - f_in: FunctionInput, map: PointType, inverse_map: PointType, + f_in: FunctionInput, + map: PointType, + inverse_map: PointType, substitute: bool = True, ) -> AnyFunction: """Inverse transpose of contravariant(). @@ -263,7 +280,9 @@ def contravariant_inverse_transpose( def identity_inverse( - f_in: FunctionInput, map: PointType, inverse_map: PointType, + f_in: FunctionInput, + map: PointType, + inverse_map: PointType, substitute: bool = True, ) -> AnyFunction: """Inverse of identity(). @@ -281,7 +300,9 @@ def identity_inverse( def l2_inverse( - f_in: FunctionInput, map: PointType, inverse_map: PointType, + f_in: FunctionInput, + map: PointType, + inverse_map: PointType, substitute: bool = True, ) -> AnyFunction: """Inverse of l2(). @@ -299,7 +320,9 @@ def l2_inverse( def covariant_inverse( - f_in: FunctionInput, map: PointType, inverse_map: PointType, + f_in: FunctionInput, + map: PointType, + inverse_map: PointType, substitute: bool = True, ) -> AnyFunction: """Inverse of covariant(). @@ -317,8 +340,7 @@ def covariant_inverse( def contravariant_inverse( - f_in: FunctionInput, map: PointType, inverse_map: PointType, - substitute: bool = True + f_in: FunctionInput, map: PointType, inverse_map: PointType, substitute: bool = True ) -> AnyFunction: """Inverse of contravariant(). @@ -335,7 +357,9 @@ def contravariant_inverse( def double_covariant_inverse( - f_in: FunctionInput, map: PointType, inverse_map: PointType, + f_in: FunctionInput, + map: PointType, + inverse_map: PointType, substitute: bool = True, ) -> AnyFunction: """Inverse of double_covariant(). @@ -353,8 +377,7 @@ def double_covariant_inverse( def double_contravariant_inverse( - f_in: FunctionInput, map: PointType, inverse_map: PointType, - substitute: bool = True + f_in: FunctionInput, map: PointType, inverse_map: PointType, substitute: bool = True ) -> AnyFunction: """Inverse of double_contravariant(). diff --git a/symfem/moments.py b/symfem/moments.py index a972afa6..b5e1ca9e 100644 --- a/symfem/moments.py +++ b/symfem/moments.py @@ -2,21 +2,19 @@ import typing -from .functionals import BaseFunctional -from .references import Reference +from symfem.functionals import BaseFunctional +from symfem.references import Reference -MomentType = typing.Tuple[typing.Type, typing.Type, int, typing.Union[str, None], - typing.Dict[str, typing.Any]] +MomentType = typing.Tuple[ + typing.Type, typing.Type, int, typing.Union[str, None], typing.Dict[str, typing.Any] +] SingleMomentTypeInput = typing.Union[ MomentType, typing.Tuple[typing.Type, typing.Type, int, str], typing.Tuple[typing.Type, typing.Type, int, typing.Dict[str, typing.Any]], typing.Tuple[typing.Type, typing.Type, int], ] -MomentTypeInput = typing.Union[ - SingleMomentTypeInput, - typing.Dict[str, SingleMomentTypeInput] -] +MomentTypeInput = typing.Union[SingleMomentTypeInput, typing.Dict[str, SingleMomentTypeInput]] def _extract_moment_data(moment_data: MomentTypeInput, sub_type: str) -> MomentType: @@ -58,7 +56,7 @@ def make_integral_moment_dofs( cells: typing.Optional[MomentTypeInput] = None, facets: typing.Optional[MomentTypeInput] = None, ridges: typing.Optional[MomentTypeInput] = None, - peaks: typing.Optional[MomentTypeInput] = None + peaks: typing.Optional[MomentTypeInput] = None, ) -> typing.List[BaseFunctional]: """Generate DOFs due to integral moments on sub entities. @@ -80,8 +78,13 @@ def make_integral_moment_dofs( # DOFs per dimension for dim, moment_data in [ - (0, vertices), (1, edges), (2, faces), (3, volumes), - (reference.tdim - 3, peaks), (reference.tdim - 2, ridges), (reference.tdim - 1, facets), + (0, vertices), + (1, edges), + (2, faces), + (3, volumes), + (reference.tdim - 3, peaks), + (reference.tdim - 2, ridges), + (reference.tdim - 1, facets), (reference.tdim, cells), ]: if moment_data is None: @@ -93,7 +96,8 @@ def make_integral_moment_dofs( for i, vs in enumerate(reference.sub_entities(dim)): sub_ref = reference.sub_entity(dim, i, False) IntegralMoment, SubElement, order, mapping, kwargs = _extract_moment_data( - moment_data, sub_ref.name) + moment_data, sub_ref.name + ) m_kwargs = {} if mapping is not None: m_kwargs["mapping"] = mapping diff --git a/symfem/piecewise_functions.py b/symfem/piecewise_functions.py index 68909c5a..0e1daf1d 100644 --- a/symfem/piecewise_functions.py +++ b/symfem/piecewise_functions.py @@ -8,13 +8,28 @@ import symfem -from .functions import (AnyFunction, FunctionInput, ScalarFunction, SympyFormat, ValuesToSubstitute, - VectorFunction, _to_sympy_format, parse_function_input) -from .geometry import (PointType, SetOfPoints, SetOfPointsInput, parse_set_of_points_input, - point_in_interval, point_in_quadrilateral, point_in_tetrahedron, - point_in_triangle) -from .references import Reference -from .symbols import AxisVariables, AxisVariablesNotSingle, t, x +from symfem.functions import ( + AnyFunction, + FunctionInput, + ScalarFunction, + SympyFormat, + ValuesToSubstitute, + VectorFunction, + _to_sympy_format, + parse_function_input, +) +from symfem.geometry import ( + PointType, + SetOfPoints, + SetOfPointsInput, + parse_set_of_points_input, + point_in_interval, + point_in_quadrilateral, + point_in_tetrahedron, + point_in_triangle, +) +from symfem.references import Reference +from symfem.symbols import AxisVariables, AxisVariablesNotSingle, t, x class PiecewiseFunction(AnyFunction): @@ -22,17 +37,16 @@ class PiecewiseFunction(AnyFunction): _pieces: typing.Dict[SetOfPoints, AnyFunction] - def __init__( - self, pieces: typing.Dict[SetOfPointsInput, FunctionInput], tdim: int - ): + def __init__(self, pieces: typing.Dict[SetOfPointsInput, FunctionInput], tdim: int): """Create a piecewise function. Args: pieces: The pieces of the function tdim: The topological dimension """ - self._pieces = {parse_set_of_points_input(shape): parse_function_input(f) - for shape, f in pieces.items()} + self._pieces = { + parse_set_of_points_input(shape): parse_function_input(f) for shape, f in pieces.items() + } self.tdim = tdim assert len(self._pieces) > 0 @@ -70,8 +84,9 @@ def as_sympy(self) -> SympyFormat: out = {} for shape, f in self._pieces.items(): fs = f.as_sympy() - assert isinstance(fs, (sympy.core.expr.Expr, tuple, - sympy.matrices.dense.MutableDenseMatrix)) + assert isinstance( + fs, (sympy.core.expr.Expr, tuple, sympy.matrices.dense.MutableDenseMatrix) + ) out[tuple(shape)] = fs return out @@ -145,7 +160,8 @@ def get_piece(self, point: PointType) -> AnyFunction: def __getitem__(self, key) -> PiecewiseFunction: """Get a component or slice of the function.""" return PiecewiseFunction( - {shape: f.__getitem__(key) for shape, f in self._pieces.items()}, self.tdim) + {shape: f.__getitem__(key) for shape, f in self._pieces.items()}, self.tdim + ) def __eq__(self, other: typing.Any) -> bool: """Check if two functions are equal.""" @@ -177,8 +193,7 @@ def __add__(self, other: typing.Any) -> PiecewiseFunction: assert shape0 == shape1 new_pieces[shape0] = f0 + f1 return PiecewiseFunction(new_pieces, self.tdim) - return PiecewiseFunction( - {shape: f + other for shape, f in self._pieces.items()}, self.tdim) + return PiecewiseFunction({shape: f + other for shape, f in self._pieces.items()}, self.tdim) def __radd__(self, other: typing.Any) -> PiecewiseFunction: """Add.""" @@ -188,8 +203,7 @@ def __radd__(self, other: typing.Any) -> PiecewiseFunction: assert shape0 == shape1 new_pieces[shape0] = f1 + f0 return PiecewiseFunction(new_pieces, self.tdim) - return PiecewiseFunction( - {shape: other + f for shape, f in self._pieces.items()}, self.tdim) + return PiecewiseFunction({shape: other + f for shape, f in self._pieces.items()}, self.tdim) def __sub__(self, other: typing.Any) -> PiecewiseFunction: """Subtract.""" @@ -199,8 +213,7 @@ def __sub__(self, other: typing.Any) -> PiecewiseFunction: assert shape0 == shape1 new_pieces[shape0] = f0 - f1 return PiecewiseFunction(new_pieces, self.tdim) - return PiecewiseFunction( - {shape: f - other for shape, f in self._pieces.items()}, self.tdim) + return PiecewiseFunction({shape: f - other for shape, f in self._pieces.items()}, self.tdim) def __rsub__(self, other: typing.Any) -> PiecewiseFunction: """Subtract.""" @@ -210,8 +223,7 @@ def __rsub__(self, other: typing.Any) -> PiecewiseFunction: assert shape0 == shape1 new_pieces[shape0] = f1 - f0 return PiecewiseFunction(new_pieces, self.tdim) - return PiecewiseFunction( - {shape: other - f for shape, f in self._pieces.items()}, self.tdim) + return PiecewiseFunction({shape: other - f for shape, f in self._pieces.items()}, self.tdim) def __truediv__(self, other: typing.Any) -> PiecewiseFunction: """Divide.""" @@ -221,8 +233,7 @@ def __truediv__(self, other: typing.Any) -> PiecewiseFunction: assert shape0 == shape1 new_pieces[shape0] = f0 / f1 return PiecewiseFunction(new_pieces, self.tdim) - return PiecewiseFunction( - {shape: f / other for shape, f in self._pieces.items()}, self.tdim) + return PiecewiseFunction({shape: f / other for shape, f in self._pieces.items()}, self.tdim) def __rtruediv__(self, other: typing.Any) -> PiecewiseFunction: """Divide.""" @@ -232,8 +243,7 @@ def __rtruediv__(self, other: typing.Any) -> PiecewiseFunction: assert shape0 == shape1 new_pieces[shape0] = f1 / f0 return PiecewiseFunction(new_pieces, self.tdim) - return PiecewiseFunction( - {shape: other / f for shape, f in self._pieces.items()}, self.tdim) + return PiecewiseFunction({shape: other / f for shape, f in self._pieces.items()}, self.tdim) def __mul__(self, other: typing.Any) -> PiecewiseFunction: """Multiply.""" @@ -243,8 +253,7 @@ def __mul__(self, other: typing.Any) -> PiecewiseFunction: assert shape0 == shape1 new_pieces[shape0] = f0 * f1 return PiecewiseFunction(new_pieces, self.tdim) - return PiecewiseFunction( - {shape: f * other for shape, f in self._pieces.items()}, self.tdim) + return PiecewiseFunction({shape: f * other for shape, f in self._pieces.items()}, self.tdim) def __rmul__(self, other: typing.Any) -> PiecewiseFunction: """Multiply.""" @@ -254,8 +263,7 @@ def __rmul__(self, other: typing.Any) -> PiecewiseFunction: assert shape0 == shape1 new_pieces[shape0] = f1 * f0 return PiecewiseFunction(new_pieces, self.tdim) - return PiecewiseFunction( - {shape: other * f for shape, f in self._pieces.items()}, self.tdim) + return PiecewiseFunction({shape: other * f for shape, f in self._pieces.items()}, self.tdim) def __matmul__(self, other: typing.Any) -> PiecewiseFunction: """Multiply.""" @@ -265,8 +273,7 @@ def __matmul__(self, other: typing.Any) -> PiecewiseFunction: assert shape0 == shape1 new_pieces[shape0] = f0 @ f1 return PiecewiseFunction(new_pieces, self.tdim) - return PiecewiseFunction( - {shape: f @ other for shape, f in self._pieces.items()}, self.tdim) + return PiecewiseFunction({shape: f @ other for shape, f in self._pieces.items()}, self.tdim) def __rmatmul__(self, other: typing.Any) -> PiecewiseFunction: """Multiply.""" @@ -276,8 +283,7 @@ def __rmatmul__(self, other: typing.Any) -> PiecewiseFunction: assert shape0 == shape1 new_pieces[shape0] = f1 @ f0 return PiecewiseFunction(new_pieces, self.tdim) - return PiecewiseFunction( - {shape: other @ f for shape, f in self._pieces.items()}, self.tdim) + return PiecewiseFunction({shape: other @ f for shape, f in self._pieces.items()}, self.tdim) def __pow__(self, other: typing.Any) -> PiecewiseFunction: """Raise to a power.""" @@ -285,18 +291,15 @@ def __pow__(self, other: typing.Any) -> PiecewiseFunction: new_pieces: typing.Dict[SetOfPointsInput, FunctionInput] = {} for (shape0, f0), (shape1, f1) in zip(self._pieces.items(), other._pieces.items()): assert shape0 == shape1 - new_pieces[shape0] = f0 ** f1 + new_pieces[shape0] = f0**f1 return PiecewiseFunction(new_pieces, self.tdim) - return PiecewiseFunction( - {shape: f ** other for shape, f in self._pieces.items()}, self.tdim) + return PiecewiseFunction({shape: f**other for shape, f in self._pieces.items()}, self.tdim) def __neg__(self) -> PiecewiseFunction: """Negate.""" return PiecewiseFunction({shape: -f for shape, f in self._pieces.items()}, self.tdim) - def subs( - self, vars: AxisVariables, values: ValuesToSubstitute - ) -> PiecewiseFunction: + def subs(self, vars: AxisVariables, values: ValuesToSubstitute) -> PiecewiseFunction: """Substitute values into the function. Args: @@ -319,7 +322,8 @@ def subs( return self.get_piece(tuple(values)).subs(vars, values) return PiecewiseFunction( - {shape: f.subs(vars, values) for shape, f in self._pieces.items()}, self.tdim) + {shape: f.subs(vars, values) for shape, f in self._pieces.items()}, self.tdim + ) def diff(self, variable: sympy.core.symbol.Symbol) -> PiecewiseFunction: """Differentiate the function. @@ -331,7 +335,8 @@ def diff(self, variable: sympy.core.symbol.Symbol) -> PiecewiseFunction: The differentiated function """ return PiecewiseFunction( - {shape: f.diff(variable) for shape, f in self._pieces.items()}, self.tdim) + {shape: f.diff(variable) for shape, f in self._pieces.items()}, self.tdim + ) def directional_derivative(self, direction: PointType) -> PiecewiseFunction: """Compute a directional derivative. @@ -343,8 +348,9 @@ def directional_derivative(self, direction: PointType) -> PiecewiseFunction: The directional derivative """ return PiecewiseFunction( - {shape: f.directional_derivative(direction) - for shape, f in self._pieces.items()}, self.tdim) + {shape: f.directional_derivative(direction) for shape, f in self._pieces.items()}, + self.tdim, + ) def jacobian_component(self, component: typing.Tuple[int, int]) -> PiecewiseFunction: """Compute a component of the jacobian. @@ -356,8 +362,8 @@ def jacobian_component(self, component: typing.Tuple[int, int]) -> PiecewiseFunc The component of the jacobian """ return PiecewiseFunction( - {shape: f.jacobian_component(component) for shape, f in self._pieces.items()}, - self.tdim) + {shape: f.jacobian_component(component) for shape, f in self._pieces.items()}, self.tdim + ) def jacobian(self, dim: int) -> PiecewiseFunction: """Compute the jacobian. @@ -369,7 +375,8 @@ def jacobian(self, dim: int) -> PiecewiseFunction: The jacobian """ return PiecewiseFunction( - {shape: f.jacobian(dim) for shape, f in self._pieces.items()}, self.tdim) + {shape: f.jacobian(dim) for shape, f in self._pieces.items()}, self.tdim + ) def dot(self, other_in: FunctionInput) -> PiecewiseFunction: """Compute the dot product with another function. @@ -381,7 +388,8 @@ def dot(self, other_in: FunctionInput) -> PiecewiseFunction: The product """ return PiecewiseFunction( - {shape: f.dot(other_in) for shape, f in self._pieces.items()}, self.tdim) + {shape: f.dot(other_in) for shape, f in self._pieces.items()}, self.tdim + ) def cross(self, other_in: FunctionInput) -> PiecewiseFunction: """Compute the cross product with another function. @@ -393,7 +401,8 @@ def cross(self, other_in: FunctionInput) -> PiecewiseFunction: The cross product """ return PiecewiseFunction( - {shape: f.cross(other_in) for shape, f in self._pieces.items()}, self.tdim) + {shape: f.cross(other_in) for shape, f in self._pieces.items()}, self.tdim + ) def div(self) -> PiecewiseFunction: """Compute the div of the function. @@ -401,8 +410,7 @@ def div(self) -> PiecewiseFunction: Returns: The divergence """ - return PiecewiseFunction( - {shape: f.div() for shape, f in self._pieces.items()}, self.tdim) + return PiecewiseFunction({shape: f.div() for shape, f in self._pieces.items()}, self.tdim) def grad(self, dim: int) -> PiecewiseFunction: """Compute the grad of the function. @@ -411,7 +419,8 @@ def grad(self, dim: int) -> PiecewiseFunction: The gradient """ return PiecewiseFunction( - {shape: f.grad(dim) for shape, f in self._pieces.items()}, self.tdim) + {shape: f.grad(dim) for shape, f in self._pieces.items()}, self.tdim + ) def curl(self) -> PiecewiseFunction: """Compute the curl of the function. @@ -419,8 +428,7 @@ def curl(self) -> PiecewiseFunction: Returns: The curl """ - return PiecewiseFunction( - {shape: f.curl() for shape, f in self._pieces.items()}, self.tdim) + return PiecewiseFunction({shape: f.curl() for shape, f in self._pieces.items()}, self.tdim) def norm(self) -> PiecewiseFunction: """Compute the norm of the function. @@ -428,12 +436,13 @@ def norm(self) -> PiecewiseFunction: Returns: The norm """ - return PiecewiseFunction( - {shape: f.norm() for shape, f in self._pieces.items()}, self.tdim) + return PiecewiseFunction({shape: f.norm() for shape, f in self._pieces.items()}, self.tdim) def integral( - self, domain: Reference, vars: AxisVariablesNotSingle = x, - dummy_vars: AxisVariablesNotSingle = t + self, + domain: Reference, + vars: AxisVariablesNotSingle = x, + dummy_vars: AxisVariablesNotSingle = t, ) -> ScalarFunction: """Compute the integral of the function. @@ -461,8 +470,7 @@ def det(self) -> PiecewiseFunction: """ if not self.is_matrix: raise AttributeError(f"'{self.__class__.__name__}' object has no attribute 'det'") - return PiecewiseFunction( - {shape: f.det() for shape, f in self._pieces.items()}, self.tdim) + return PiecewiseFunction({shape: f.det() for shape, f in self._pieces.items()}, self.tdim) def transpose(self) -> PiecewiseFunction: """Compute the transpose. @@ -473,7 +481,8 @@ def transpose(self) -> PiecewiseFunction: if not self.is_matrix: raise AttributeError(f"'{self.__class__.__name__}' object has no attribute 'transpose'") return PiecewiseFunction( - {shape: f.transpose() for shape, f in self._pieces.items()}, self.tdim) + {shape: f.transpose() for shape, f in self._pieces.items()}, self.tdim + ) def map_pieces(self, fwd_map: PointType): """Map the function's pieces. @@ -492,8 +501,10 @@ def map_pieces(self, fwd_map: PointType): assert isinstance(pt, tuple) nshape.append(pt) new_pieces[tuple(nshape)] = f - self._pieces = {parse_set_of_points_input(shape): parse_function_input(f) - for shape, f in new_pieces.items()} + self._pieces = { + parse_set_of_points_input(shape): parse_function_input(f) + for shape, f in new_pieces.items() + } @property def shape(self) -> typing.Tuple[int, ...]: @@ -505,8 +516,11 @@ def shape(self) -> typing.Tuple[int, ...]: return self.first_piece.shape def plot_values( - self, reference: Reference, img: typing.Any, - value_scale: sympy.core.expr.Expr = sympy.Integer(1), n: int = 6 + self, + reference: Reference, + img: typing.Any, + value_scale: sympy.core.expr.Expr = sympy.Integer(1), + n: int = 6, ): """Plot the function's values. @@ -516,7 +530,8 @@ def plot_values( value_scale: The scale factor for the function values n: The number of points per side for plotting """ - from .plotting import Picture + from symfem.plotting import Picture + assert isinstance(img, Picture) for shape, f in self._pieces.items(): @@ -530,7 +545,8 @@ def with_floats(self) -> AnyFunction: A version the function with floats as coefficients """ return PiecewiseFunction( - {shape: f.with_floats() for shape, f in self._pieces.items()}, self.tdim) + {shape: f.with_floats() for shape, f in self._pieces.items()}, self.tdim + ) def maximum_degree(self, cell: symfem.references.Reference) -> int: """Return the maximum degree of the function on a reference cell. @@ -549,7 +565,8 @@ def maximum_degree(self, cell: symfem.references.Reference) -> int: def _piece_reference(tdim, shape): """Create a reference element for a single piece.""" - from .create import create_reference + from symfem.create import create_reference + if tdim == 1: return create_reference("interval", shape) elif tdim == 2: diff --git a/symfem/plotting.py b/symfem/plotting.py index f7c2a6be..8edac690 100644 --- a/symfem/plotting.py +++ b/symfem/plotting.py @@ -5,13 +5,20 @@ import sympy -from .functions import AnyFunction, VectorFunction -from .geometry import (PointType, PointTypeInput, SetOfPoints, SetOfPointsInput, parse_point_input, - parse_set_of_points_input) +from symfem.functions import AnyFunction, VectorFunction +from symfem.geometry import ( + PointType, + PointTypeInput, + SetOfPoints, + SetOfPointsInput, + parse_point_input, + parse_set_of_points_input, +) PointOrFunction = typing.Union[PointTypeInput, AnyFunction] SetOfPointsOrFunctions = typing.Union[ - typing.List[PointOrFunction], typing.Tuple[PointOrFunction, ...]] + typing.List[PointOrFunction], typing.Tuple[PointOrFunction, ...] +] def tex_font_size(n: int): @@ -117,9 +124,7 @@ def __init__(self): pass @abstractmethod - def as_svg( - self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]] - ) -> str: + def as_svg(self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]]) -> str: """Return SVG format. Args: @@ -131,9 +136,7 @@ def as_svg( pass @abstractmethod - def as_tikz( - self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]] - ) -> str: + def as_tikz(self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]]) -> str: """Return Tikz format. Args: @@ -190,10 +193,7 @@ def maxy(self) -> sympy.core.expr.Expr: class Line(PictureElement): """A line.""" - def __init__( - self, start: PointType, end: PointType, color: str, - width: float - ): + def __init__(self, start: PointType, end: PointType, color: str, width: float): """Create a line. Args: @@ -208,9 +208,7 @@ def __init__( self.color = color self.width = width - def as_svg( - self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]] - ) -> str: + def as_svg(self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]]) -> str: """Return SVG format. Args: @@ -221,12 +219,12 @@ def as_svg( """ s = map_pt(self.start) e = map_pt(self.end) - return (f"\n") + return ( + f"\n" + ) - def as_tikz( - self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]] - ) -> str: + def as_tikz(self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]]) -> str: """Return Tikz format. Args: @@ -237,8 +235,10 @@ def as_tikz( """ s = map_pt(self.start) e = map_pt(self.end) - return (f"\\draw[{colors.get_tikz_name(self.color)},line width={self.width * 0.2}pt," - f"line cap=round] ({s[0]},{s[1]}) -- ({e[0]},{e[1]});\n") + return ( + f"\\draw[{colors.get_tikz_name(self.color)},line width={self.width * 0.2}pt," + f"line cap=round] ({s[0]},{s[1]}) -- ({e[0]},{e[1]});\n" + ) @property def points(self) -> SetOfPoints: @@ -254,8 +254,13 @@ class Bezier(PictureElement): """A Bezier curve.""" def __init__( - self, start: PointType, mid1: PointType, mid2: PointType, end: PointType, color: str, - width: float + self, + start: PointType, + mid1: PointType, + mid2: PointType, + end: PointType, + color: str, + width: float, ): """Create a Bezier curve. @@ -275,9 +280,7 @@ def __init__( self.color = color self.width = width - def as_svg( - self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]] - ) -> str: + def as_svg(self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]]) -> str: """Return SVG format. Args: @@ -290,13 +293,13 @@ def as_svg( m1 = map_pt(self.mid1) m2 = map_pt(self.mid2) e = map_pt(self.end) - return (f"\n") + return ( + f"\n" + ) - def as_tikz( - self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]] - ) -> str: + def as_tikz(self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]]) -> str: """Return Tikz format. Args: @@ -309,9 +312,11 @@ def as_tikz( m1 = map_pt(self.mid1) m2 = map_pt(self.mid2) e = map_pt(self.end) - return (f"\\draw[{colors.get_tikz_name(self.color)},line width={self.width * 0.2}pt," - f"line cap=round] ({s[0]},{s[1]}) .. controls ({m1[0]},{m1[1]}) " - f"and ({m2[0]},{m2[1]}) .. ({e[0]},{e[1]});\n") + return ( + f"\\draw[{colors.get_tikz_name(self.color)},line width={self.width * 0.2}pt," + f"line cap=round] ({s[0]},{s[1]}) .. controls ({m1[0]},{m1[1]}) " + f"and ({m2[0]},{m2[1]}) .. ({e[0]},{e[1]});\n" + ) @property def points(self) -> SetOfPoints: @@ -326,10 +331,7 @@ def points(self) -> SetOfPoints: class Arrow(PictureElement): """An arrow.""" - def __init__( - self, start: PointType, end: PointType, color: str, - width: float - ): + def __init__(self, start: PointType, end: PointType, color: str, width: float): """Create an arrow. Args: @@ -344,9 +346,7 @@ def __init__( self.color = color self.width = width - def as_svg( - self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]] - ) -> str: + def as_svg(self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]]) -> str: """Return SVG format. Args: @@ -359,8 +359,10 @@ def as_svg( s = map_pt(self.start) e = map_pt(self.end) - out += (f"\n") + out += ( + f"\n" + ) ve = VectorFunction(self.end) vs = VectorFunction(self.start) @@ -374,14 +376,14 @@ def as_svg( pt_s = pt.as_sympy() assert isinstance(pt_s, tuple) m = map_pt(pt_s) - out += (f"\n") + out += ( + f"\n" + ) return out - def as_tikz( - self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]] - ) -> str: + def as_tikz(self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]]) -> str: """Return Tikz format. Args: @@ -392,9 +394,11 @@ def as_tikz( """ s = map_pt(self.start) e = map_pt(self.end) - return (f"\\draw[-stealth,{colors.get_tikz_name(self.color)}," - f"line width={self.width * 0.2}pt,line cap=round] " - f"({s[0]},{s[1]}) -- ({e[0]},{e[1]});\n") + return ( + f"\\draw[-stealth,{colors.get_tikz_name(self.color)}," + f"line width={self.width * 0.2}pt,line cap=round] " + f"({s[0]},{s[1]}) -- ({e[0]},{e[1]});\n" + ) @property def points(self) -> SetOfPoints: @@ -410,8 +414,16 @@ class NCircle(PictureElement): """A circle containing a number.""" def __init__( - self, centre: PointType, number: int, color: str, text_color: str, fill_color: str, - radius: float, font_size: typing.Union[int, None], width: float, font: str + self, + centre: PointType, + number: int, + color: str, + text_color: str, + fill_color: str, + radius: float, + font_size: typing.Union[int, None], + width: float, + font: str, ): """Create a circle containing a number. @@ -444,9 +456,7 @@ def __init__( self.font_size = font_size self.width = width - def as_svg( - self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]] - ) -> str: + def as_svg(self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]]) -> str: """Return SVG format. Args: @@ -457,15 +467,15 @@ def as_svg( """ c = map_pt(self.centre) - return (f"\n" - f"" - f"{self.number}\n") + return ( + f"\n" + f"' + f"{self.number}\n" + ) - def as_tikz( - self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]] - ) -> str: + def as_tikz(self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]]) -> str: """Return Tikz format. Args: @@ -475,11 +485,13 @@ def as_tikz( A Tikz string """ c = map_pt(self.centre) - return (f"\\draw[{colors.get_tikz_name(self.color)},line width={self.width * 0.2}pt," - f"fill={colors.get_tikz_name(self.fill_color)}] " - f"({c[0]},{c[1]}) circle ({self.radius * 0.2}pt);\n" - f"\\node[{colors.get_tikz_name(self.text_color)},anchor=center] " - f"at ({c[0]},{c[1]}) {{{tex_font_size(self.font_size)} {self.number}}};") + return ( + f"\\draw[{colors.get_tikz_name(self.color)},line width={self.width * 0.2}pt," + f"fill={colors.get_tikz_name(self.fill_color)}] " + f"({c[0]},{c[1]}) circle ({self.radius * 0.2}pt);\n" + f"\\node[{colors.get_tikz_name(self.text_color)},anchor=center] " + f"at ({c[0]},{c[1]}) {{{tex_font_size(self.font_size)} {self.number}}};" + ) @property def points(self) -> SetOfPoints: @@ -488,7 +500,7 @@ def points(self) -> SetOfPoints: Returns: A set of points """ - return (self.centre, ) + return (self.centre,) class Fill(PictureElement): @@ -506,9 +518,7 @@ def __init__(self, vertices: SetOfPoints, color: str, opacity: float): self.color = color self.opacity = opacity - def as_svg( - self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]] - ) -> str: + def as_svg(self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]]) -> str: """Return SVG format. Args: @@ -521,9 +531,7 @@ def as_svg( ptstring = " ".join(f"{p[0]},{p[1]}" for p in pts) return f"" - def as_tikz( - self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]] - ) -> str: + def as_tikz(self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]]) -> str: """Return Tikz format. Args: @@ -533,8 +541,10 @@ def as_tikz( A Tikz string """ vs = [map_pt(v) for v in self.vertices] - return (f"\\fill[{colors.get_tikz_name(self.color)},opacity={self.opacity}]" - " " + " -- ".join([f"({v[0]},{v[1]})" for v in vs]) + " -- cycle;") + return ( + f"\\fill[{colors.get_tikz_name(self.color)},opacity={self.opacity}]" + " " + " -- ".join([f"({v[0]},{v[1]})" for v in vs]) + " -- cycle;" + ) @property def points(self) -> SetOfPoints: @@ -549,8 +559,7 @@ def points(self) -> SetOfPoints: class Math(PictureElement): """A mathematical symbol.""" - def __init__(self, point: PointType, math: str, color: str, font_size: int, - anchor: str): + def __init__(self, point: PointType, math: str, color: str, font_size: int, anchor: str): """Create a filled polygon. Args: @@ -565,13 +574,19 @@ def __init__(self, point: PointType, math: str, color: str, font_size: int, self.color = color self.font_size = font_size assert anchor in [ - "center", "north", "south", "east", "west", - "north east", "north west", "south east", "south west"] + "center", + "north", + "south", + "east", + "west", + "north east", + "north west", + "south east", + "south west", + ] self.anchor = anchor - def as_svg( - self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]] - ) -> str: + def as_svg(self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]]) -> str: """Return SVG format. Args: @@ -600,13 +615,13 @@ def as_svg( c = map_pt(self.point) - return (f"" - f"{self.math}\n") + return ( + f"" + f"{self.math}\n" + ) - def as_tikz( - self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]] - ) -> str: + def as_tikz(self, map_pt: typing.Callable[[PointType], typing.Tuple[float, float]]) -> str: """Return Tikz format. Args: @@ -616,8 +631,10 @@ def as_tikz( A Tikz string """ p = map_pt(self.point) - return (f"\\node[{colors.get_tikz_name(self.color)},anchor={self.anchor}] " - f"at ({p[0]},{p[1]}) {{{tex_font_size(self.font_size)}${self.math}$}};") + return ( + f"\\node[{colors.get_tikz_name(self.color)},anchor={self.anchor}] " + f"at ({p[0]},{p[1]}) {{{tex_font_size(self.font_size)}${self.math}$}};" + ) @property def points(self) -> SetOfPoints: @@ -626,7 +643,7 @@ def points(self) -> SetOfPoints: Returns: A set of points """ - return (self.point, ) + return (self.point,) class Picture: @@ -635,12 +652,17 @@ class Picture: axes_3d: SetOfPoints def __init__( - self, padding: sympy.core.expr.Expr = sympy.Integer(25), scale: typing.Optional[int] = None, - width: typing.Optional[int] = None, height: typing.Optional[int] = None, + self, + padding: sympy.core.expr.Expr = sympy.Integer(25), + scale: typing.Optional[int] = None, + width: typing.Optional[int] = None, + height: typing.Optional[int] = None, axes_3d: typing.Optional[SetOfPointsInput] = None, dof_arrow_size: typing.Union[int, sympy.core.expr.Expr] = 1, - title: typing.Optional[str] = None, desc: typing.Optional[str] = None, - svg_metadata: typing.Optional[str] = None, tex_comment: typing.Optional[str] = None + title: typing.Optional[str] = None, + desc: typing.Optional[str] = None, + svg_metadata: typing.Optional[str] = None, + tex_comment: typing.Optional[str] = None, ): """Create a picture. @@ -659,7 +681,8 @@ def __init__( self._default_axes: SetOfPoints = ( (sympy.Integer(1), -sympy.Rational(2, 25)), (sympy.Rational(1, 2), sympy.Rational(1, 5)), - (sympy.Integer(0), sympy.Integer(1))) + (sympy.Integer(0), sympy.Integer(1)), + ) self.elements: typing.List[PictureElement] = [] self.padding = padding self.scale = scale @@ -674,21 +697,25 @@ def __init__( self.desc = desc if svg_metadata is None: import symfem + self.svg_metadata = ( "\n") + "-->\n" + ) else: assert isinstance(svg_metadata, str) self.svg_metadata = svg_metadata if tex_comment is None: import symfem + self.tex_comment = ( "% This diagram was created using Symfem\n" f"% {symfem.__github__}\n" - f"% {symfem.__citation__}\n\n") + f"% {symfem.__citation__}\n\n" + ) else: assert isinstance(tex_comment, str) self.tex_comment = tex_comment @@ -732,7 +759,8 @@ def to_2d(self, p: PointType) -> PointType: if len(p) == 3: return ( self.axes_3d[0][0] * p[0] + self.axes_3d[1][0] * p[1] + self.axes_3d[2][0] * p[2], - self.axes_3d[0][1] * p[0] + self.axes_3d[1][1] * p[1] + self.axes_3d[2][1] * p[2]) + self.axes_3d[0][1] * p[0] + self.axes_3d[1][1] * p[1] + self.axes_3d[2][1] * p[2], + ) raise ValueError(f"Unsupported gdim: {len(p)}") def parse_point(self, p: PointOrFunction) -> PointType: @@ -752,8 +780,11 @@ def parse_point(self, p: PointOrFunction) -> PointType: return self.to_2d(parse_point_input(p)) def add_line( - self, start: PointOrFunction, end: PointOrFunction, color: str = colors.BLACK, - width: float = 4.0 + self, + start: PointOrFunction, + end: PointOrFunction, + color: str = colors.BLACK, + width: float = 4.0, ): """Add a line to the picture. @@ -766,8 +797,13 @@ def add_line( self.elements.append(Line(self.parse_point(start), self.parse_point(end), color, width)) def add_bezier( - self, start: PointOrFunction, mid1: PointOrFunction, mid2: PointOrFunction, - end: PointOrFunction, color: str = colors.BLACK, width: float = 4.0 + self, + start: PointOrFunction, + mid1: PointOrFunction, + mid2: PointOrFunction, + end: PointOrFunction, + color: str = colors.BLACK, + width: float = 4.0, ): """Add a Bezier curve to the picture. @@ -779,13 +815,23 @@ def add_bezier( color: The color of the Bezier curve width: The width of the Bezier curve """ - self.elements.append(Bezier( - self.parse_point(start), self.parse_point(mid1), self.parse_point(mid2), - self.parse_point(end), color, width)) + self.elements.append( + Bezier( + self.parse_point(start), + self.parse_point(mid1), + self.parse_point(mid2), + self.parse_point(end), + color, + width, + ) + ) def add_arrow( - self, start: PointOrFunction, end: PointOrFunction, color: str = colors.BLACK, - width: float = 4.0 + self, + start: PointOrFunction, + end: PointOrFunction, + color: str = colors.BLACK, + width: float = 4.0, ): """Add an arrow to the picture. @@ -797,9 +843,7 @@ def add_arrow( """ self.elements.append(Arrow(self.parse_point(start), self.parse_point(end), color, width)) - def add_dof_marker( - self, point: PointOrFunction, number: int, color: str, bold: bool = True - ): + def add_dof_marker(self, point: PointOrFunction, number: int, color: str, bold: bool = True): """Add a DOF marker. Args: @@ -814,8 +858,13 @@ def add_dof_marker( self.add_ncircle(point, number, color, color, colors.WHITE) def add_dof_arrow( - self, point: PointOrFunction, direction: PointOrFunction, number: int, - color: str = colors.PURPLE, shifted: bool = False, bold: bool = True + self, + point: PointOrFunction, + direction: PointOrFunction, + number: int, + color: str = colors.PURPLE, + shifted: bool = False, + bold: bool = True, ): """Add a DOF arrow. @@ -837,10 +886,16 @@ def add_dof_arrow( self.add_dof_marker(start, number, color, bold) def add_ncircle( - self, centre: PointOrFunction, number: int, color: str = "red", - text_color: str = colors.BLACK, fill_color: str = colors.WHITE, radius: float = 20.0, - font_size: typing.Optional[int] = None, width: float = 4.0, - font: str = "'Varela Round',sans-serif" + self, + centre: PointOrFunction, + number: int, + color: str = "red", + text_color: str = colors.BLACK, + fill_color: str = colors.WHITE, + radius: float = 20.0, + font_size: typing.Optional[int] = None, + width: float = 4.0, + font: str = "'Varela Round',sans-serif", ): """Add a numbered circle to the picture. @@ -855,12 +910,28 @@ def add_ncircle( width: The width of the line font: The font """ - self.elements.append(NCircle( - self.parse_point(centre), number, color, text_color, fill_color, radius, font_size, - width, font)) - - def add_math(self, point: PointTypeInput, math: str, color: str = colors.BLACK, - font_size: int = 35, anchor="center"): + self.elements.append( + NCircle( + self.parse_point(centre), + number, + color, + text_color, + fill_color, + radius, + font_size, + width, + font, + ) + ) + + def add_math( + self, + point: PointTypeInput, + math: str, + color: str = colors.BLACK, + font_size: int = 35, + anchor="center", + ): """Create mathematical symbol. Args: @@ -872,9 +943,7 @@ def add_math(self, point: PointTypeInput, math: str, color: str = colors.BLACK, """ self.elements.append(Math(self.parse_point(point), math, color, font_size, anchor)) - def add_fill( - self, vertices: SetOfPointsOrFunctions, color: str = "red", opacity: float = 1.0 - ): + def add_fill(self, vertices: SetOfPointsOrFunctions, color: str = "red", opacity: float = 1.0): """Add a filled polygon to the picture. Args: @@ -884,9 +953,13 @@ def add_fill( """ self.elements.append(Fill(tuple(self.parse_point(p) for p in vertices), color, opacity)) - def compute_scale(self, unit: str = "px", reverse_y: bool = True) -> typing.Tuple[ - sympy.core.expr.Expr, sympy.core.expr.Expr, sympy.core.expr.Expr, - typing.Callable[[PointType], typing.Tuple[float, float]] + def compute_scale( + self, unit: str = "px", reverse_y: bool = True + ) -> typing.Tuple[ + sympy.core.expr.Expr, + sympy.core.expr.Expr, + sympy.core.expr.Expr, + typing.Callable[[PointType], typing.Tuple[float, float]], ]: """Compute the scale and size of the picture. @@ -927,17 +1000,21 @@ def compute_scale(self, unit: str = "px", reverse_y: bool = True) -> typing.Tupl height = 2 * self.padding + (maxy - miny) * scale if reverse_y: + def map_pt(pt: PointType) -> typing.Tuple[float, float]: """Map a point.""" return ( float(self.padding + (pt[0] - minx) * scale), - float(height - self.padding - (pt[1] - miny) * scale)) + float(height - self.padding - (pt[1] - miny) * scale), + ) else: + def map_pt(pt: PointType) -> typing.Tuple[float, float]: """Map a point.""" return ( float(self.padding + (pt[0] - minx) * scale), - float(self.padding + (pt[1] - miny) * scale)) + float(self.padding + (pt[1] - miny) * scale), + ) return scale, height, width, map_pt @@ -953,8 +1030,10 @@ def as_svg(self, filename: typing.Optional[str] = None) -> str: scale, height, width, map_pt = self.compute_scale("px") assert filename is None or filename.endswith(".svg") - img = (f"\n") + img = ( + f"\n" + ) if self.title is not None: img += f"{self.title}\n" if self.desc is not None: @@ -973,8 +1052,13 @@ def as_svg(self, filename: typing.Optional[str] = None) -> str: return img - def as_png(self, filename: str, png_scale: typing.Optional[float] = None, - png_width: typing.Optional[int] = None, png_height: typing.Optional[int] = None): + def as_png( + self, + filename: str, + png_scale: typing.Optional[float] = None, + png_width: typing.Optional[int] = None, + png_height: typing.Optional[int] = None, + ): """Convert to a PNG. Args: @@ -986,8 +1070,7 @@ def as_png(self, filename: str, png_scale: typing.Optional[float] = None, try: from cairosvg import svg2png except ImportError: - raise ImportError("CairoSVG is needed for plotting PNGs" - " (pip install CairoSVG)") + raise ImportError("CairoSVG is needed for plotting PNGs" " (pip install CairoSVG)") if png_scale is not None: assert png_width is None @@ -1031,8 +1114,11 @@ def as_tikz(self, filename: typing.Optional[str] = None) -> str: return tikz - def save(self, filename: typing.Union[str, typing.List[str]], - plot_options: typing.Dict[str, typing.Any] = {}): + def save( + self, + filename: typing.Union[str, typing.List[str]], + plot_options: typing.Dict[str, typing.Any] = {}, + ): """Save the picture as a file. Args: diff --git a/symfem/polynomials/__init__.py b/symfem/polynomials/__init__.py index c5e8e09d..d0aae84e 100644 --- a/symfem/polynomials/__init__.py +++ b/symfem/polynomials/__init__.py @@ -3,9 +3,22 @@ from .dual import l2_dual from .legendre import orthogonal_basis, orthonormal_basis from .lobatto import lobatto_basis, lobatto_dual_basis -from .polysets import (Hcurl_polynomials, Hcurl_quolynomials, Hcurl_serendipity, Hdiv_polynomials, - Hdiv_quolynomials, Hdiv_serendipity, polynomial_set_1d, - polynomial_set_vector, prism_polynomial_set_1d, prism_polynomial_set_vector, - pyramid_polynomial_set_1d, pyramid_polynomial_set_vector, quolynomial_set_1d, - quolynomial_set_vector, serendipity_indices, serendipity_set_1d, - serendipity_set_vector) +from .polysets import ( + Hcurl_polynomials, + Hcurl_quolynomials, + Hcurl_serendipity, + Hdiv_polynomials, + Hdiv_quolynomials, + Hdiv_serendipity, + polynomial_set_1d, + polynomial_set_vector, + prism_polynomial_set_1d, + prism_polynomial_set_vector, + pyramid_polynomial_set_1d, + pyramid_polynomial_set_vector, + quolynomial_set_1d, + quolynomial_set_vector, + serendipity_indices, + serendipity_set_1d, + serendipity_set_vector, +) diff --git a/symfem/polynomials/dual.py b/symfem/polynomials/dual.py index 63e457a3..a078612e 100644 --- a/symfem/polynomials/dual.py +++ b/symfem/polynomials/dual.py @@ -18,6 +18,7 @@ def l2_dual(cell: str, poly: typing.List[ScalarFunction]) -> typing.List[ScalarF The L2 dual polynomials """ from ..create import create_reference + reference = create_reference(cell) matrix = sympy.Matrix([[(p * q).integral(reference) for q in poly] for p in poly]) diff --git a/symfem/polynomials/legendre.py b/symfem/polynomials/legendre.py index 1133f6e3..4fe03a79 100644 --- a/symfem/polynomials/legendre.py +++ b/symfem/polynomials/legendre.py @@ -8,7 +8,9 @@ from ..symbols import AxisVariablesNotSingle, x -def _jrc(a, n) -> typing.Tuple[ +def _jrc( + a, n +) -> typing.Tuple[ sympy.core.expr.Expr, sympy.core.expr.Expr, sympy.core.expr.Expr, @@ -25,7 +27,7 @@ def _jrc(a, n) -> typing.Tuple[ return ( sympy.Rational((a + 2 * n + 1) * (a + 2 * n + 2), 2 * (n + 1) * (a + n + 1)), sympy.Rational(a * a * (a + 2 * n + 1), 2 * (n + 1) * (a + n + 1) * (a + 2 * n)), - sympy.Rational(n * (a + n) * (a + 2 * n + 2), (n + 1) * (a + n + 1) * (a + 2 * n)) + sympy.Rational(n * (a + n) * (a + 2 * n + 2), (n + 1) * (a + n + 1) * (a + 2 * n)), ) @@ -44,8 +46,9 @@ def orthogonal_basis_interval( """ assert len(variables) == 1 - poly = [[ScalarFunction(1 if d == 0 else 0) for i in range(order + 1)] - for d in range(derivs + 1)] + poly = [ + [ScalarFunction(1 if d == 0 else 0) for i in range(order + 1)] for d in range(derivs + 1) + ] for dx in range(derivs + 1): for i in range(1, order + 1): poly[dx][i] = poly[dx][i - 1] * (2 * variables[0] - 1) * (2 * i - 1) / i @@ -77,12 +80,13 @@ def index(p: int, q: int) -> int: d_index = index - poly = [[ScalarFunction(0) for i in range((order + 1) * (order + 2) // 2)] - for d in range((derivs + 1) * (derivs + 2) // 2)] + poly = [ + [ScalarFunction(0) for i in range((order + 1) * (order + 2) // 2)] + for d in range((derivs + 1) * (derivs + 2) // 2) + ] for dx in range(derivs + 1): for dy in range(derivs + 1 - dx): - for p in range(order + 1): if p == 0: poly[d_index(dx, dy)][index(0, p)] = ScalarFunction(1 if dx == dy == 0 else 0) @@ -91,7 +95,8 @@ def index(p: int, q: int) -> int: poly[d_index(dx, dy)][index(0, p)] = ( poly[d_index(dx, dy)][index(0, p - 1)] - * (2 * variables[0] + variables[1] - 1) * (2 - pinv) + * (2 * variables[0] + variables[1] - 1) + * (2 - pinv) ) if dy > 0: poly[d_index(dx, dy)][index(0, p)] += ( @@ -104,23 +109,29 @@ def index(p: int, q: int) -> int: if p > 1: poly[d_index(dx, dy)][index(0, p)] -= ( poly[d_index(dx, dy)][index(0, p - 2)] - * (1 - variables[1]) ** 2 * (1 - pinv) + * (1 - variables[1]) ** 2 + * (1 - pinv) ) if dy > 0: poly[d_index(dx, dy)][index(0, p)] += ( - 2 * dy * (1 - variables[1]) - * poly[d_index(dx, dy - 1)][index(0, p - 2)] * (1 - pinv) + 2 + * dy + * (1 - variables[1]) + * poly[d_index(dx, dy - 1)][index(0, p - 2)] + * (1 - pinv) ) if dy > 1: poly[d_index(dx, dy)][index(0, p)] -= ( - dy * (dy - 1) * poly[d_index(dx, dy - 2)][index(0, p - 2)] + dy + * (dy - 1) + * poly[d_index(dx, dy - 2)][index(0, p - 2)] * (1 - pinv) ) for q in range(1, order - p + 1): a, b, c = _jrc(2 * p + 1, q - 1) - poly[d_index(dx, dy)][index(q, p)] = ( - poly[d_index(dx, dy)][index(q - 1, p)] * ((2 * variables[1] - 1) * a + b) + poly[d_index(dx, dy)][index(q, p)] = poly[d_index(dx, dy)][index(q - 1, p)] * ( + (2 * variables[1] - 1) * a + b ) if q > 1: poly[d_index(dx, dy)][index(q, p)] -= ( @@ -154,8 +165,10 @@ def d_index(p: int, q: int) -> int: p0 = orthogonal_basis_interval(order, derivs, [variables[0]]) p1 = orthogonal_basis_interval(order, derivs, [variables[1]]) - poly = [[ScalarFunction(0) for i in range((order + 1) ** 2)] - for d in range((derivs + 1) * (derivs + 2) // 2)] + poly = [ + [ScalarFunction(0) for i in range((order + 1) ** 2)] + for d in range((derivs + 1) * (derivs + 2) // 2) + ] for i in range(derivs + 1): for j in range(derivs + 1 - i): poly[d_index(i, j)] = [a * b for a in p0[i] for b in p1[j]] @@ -183,26 +196,31 @@ def index(p: int, q: int, r: int) -> int: d_index = index - poly = [[ScalarFunction(0) for i in range((order + 1) * (order + 2) * (order + 3) // 6)] - for d in range((derivs + 1) * (derivs + 2) * (derivs + 3) // 6)] + poly = [ + [ScalarFunction(0) for i in range((order + 1) * (order + 2) * (order + 3) // 6)] + for d in range((derivs + 1) * (derivs + 2) * (derivs + 3) // 6) + ] for dx in range(derivs + 1): for dy in range(derivs + 1 - dx): for dz in range(derivs + 1 - dx - dy): - for p in range(order + 1): if p == 0: poly[d_index(dx, dy, dz)][index(0, 0, p)] = ScalarFunction( - 1 if dx == dy == dz == 0 else 0) + 1 if dx == dy == dz == 0 else 0 + ) if p > 0: invp = sympy.Rational(1, p) poly[d_index(dx, dy, dz)][index(0, 0, p)] = ( poly[d_index(dx, dy, dz)][index(0, 0, p - 1)] - * (2 * variables[0] + variables[1] + variables[2] - 1) * (2 - invp) + * (2 * variables[0] + variables[1] + variables[2] - 1) + * (2 - invp) ) if dx > 0: poly[d_index(dx, dy, dz)][index(0, 0, p)] += ( poly[d_index(dx - 1, dy, dz)][index(0, 0, p - 1)] - * 2 * dx * (2 - invp) + * 2 + * dx + * (2 - invp) ) if dy > 0: poly[d_index(dx, dy, dz)][index(0, 0, p)] += ( @@ -216,74 +234,89 @@ def index(p: int, q: int, r: int) -> int: if p > 1: poly[d_index(dx, dy, dz)][index(0, 0, p)] -= ( poly[d_index(dx, dy, dz)][index(0, 0, p - 2)] - * (variables[1] + variables[2] - 1) ** 2 * (1 - invp) + * (variables[1] + variables[2] - 1) ** 2 + * (1 - invp) ) if dy > 0: poly[d_index(dx, dy, dz)][index(0, 0, p)] -= ( poly[d_index(dx, dy - 1, dz)][index(0, 0, p - 2)] - * 2 * (variables[1] + variables[2] - 1) * dy * (1 - invp) + * 2 + * (variables[1] + variables[2] - 1) + * dy + * (1 - invp) ) if dy > 1: poly[d_index(dx, dy, dz)][index(0, 0, p)] -= ( poly[d_index(dx, dy - 2, dz)][index(0, 0, p - 2)] - * dy * (dy - 1) * (1 - invp) + * dy + * (dy - 1) + * (1 - invp) ) if dz > 0: poly[d_index(dx, dy, dz)][index(0, 0, p)] -= ( poly[d_index(dx, dy, dz - 1)][index(0, 0, p - 2)] - * 2 * (variables[1] + variables[2] - 1) * dz * (1 - invp) + * 2 + * (variables[1] + variables[2] - 1) + * dz + * (1 - invp) ) if dz > 1: poly[d_index(dx, dy, dz)][index(0, 0, p)] -= ( poly[d_index(dx, dy, dz - 2)][index(0, 0, p - 2)] - * dz * (dz - 1) * (1 - invp) + * dz + * (dz - 1) + * (1 - invp) ) if dz > 0 and dy > 0: poly[d_index(dx, dy, dz)][index(0, 0, p)] -= ( poly[d_index(dx, dy - 1, dz - 1)][index(0, 0, p - 2)] - * 2 * dz * dy * (1 - invp) + * 2 + * dz + * dy + * (1 - invp) ) for q in range(order - p + 1): if q > 0: a, b, c = _jrc(2 * p + 1, q - 1) - poly[d_index(dx, dy, dz)][index(0, q, p)] = ( - poly[d_index(dx, dy, dz)][index(0, q - 1, p)] - * ((2 * variables[1] + variables[2] - 1) * a - + (1 - variables[2]) * b) - ) + poly[d_index(dx, dy, dz)][index(0, q, p)] = poly[d_index(dx, dy, dz)][ + index(0, q - 1, p) + ] * ((2 * variables[1] + variables[2] - 1) * a + (1 - variables[2]) * b) if dy > 0: poly[d_index(dx, dy, dz)][index(0, q, p)] += ( - poly[d_index(dx, dy - 1, dz)][index(0, q - 1, p)] - * 2 * a * dy + poly[d_index(dx, dy - 1, dz)][index(0, q - 1, p)] * 2 * a * dy ) if dz > 0: poly[d_index(dx, dy, dz)][index(0, q, p)] += ( - poly[d_index(dx, dy, dz - 1)][index(0, q - 1, p)] - * (a - b) * dz + poly[d_index(dx, dy, dz - 1)][index(0, q - 1, p)] * (a - b) * dz ) if q > 1: poly[d_index(dx, dy, dz)][index(0, q, p)] -= ( poly[d_index(dx, dy, dz)][index(0, q - 2, p)] - * (1 - variables[2]) ** 2 * c + * (1 - variables[2]) ** 2 + * c ) if dz > 0: poly[d_index(dx, dy, dz)][index(0, q, p)] += ( poly[d_index(dx, dy, dz - 1)][index(0, q - 2, p)] - * 2 * (1 - variables[2]) * dz * c + * 2 + * (1 - variables[2]) + * dz + * c ) if dz > 1: poly[d_index(dx, dy, dz)][index(0, q, p)] -= ( poly[d_index(dx, dy, dz - 2)][index(0, q - 2, p)] - * dz * (dz - 1) * c + * dz + * (dz - 1) + * c ) for r in range(1, order - p - q + 1): a, b, c = _jrc(2 * p + 2 * q + 2, r - 1) - poly[d_index(dx, dy, dz)][index(r, q, p)] = ( - poly[d_index(dx, dy, dz)][index(r - 1, q, p)] - * ((variables[2] * 2 - 1) * a + b) - ) + poly[d_index(dx, dy, dz)][index(r, q, p)] = poly[d_index(dx, dy, dz)][ + index(r - 1, q, p) + ] * ((variables[2] * 2 - 1) * a + b) if dz > 0: poly[d_index(dx, dy, dz)][index(r, q, p)] += ( poly[d_index(dx, dy, dz - 1)][index(r - 1, q, p)] * 2 * a * dz @@ -320,8 +353,10 @@ def d_index(p: int, q: int, r: int) -> int: p0 = orthogonal_basis_interval(order, derivs, [variables[0]]) p1 = orthogonal_basis_interval(order, derivs, [variables[1]]) p2 = orthogonal_basis_interval(order, derivs, [variables[2]]) - poly = [[ScalarFunction(0) for i in range((order + 1) ** 3)] - for d in range((derivs + 1) * (derivs + 2) * (derivs + 3) // 6)] + poly = [ + [ScalarFunction(0) for i in range((order + 1) ** 3)] + for d in range((derivs + 1) * (derivs + 2) * (derivs + 3) // 6) + ] for i in range(derivs + 1): for j in range(derivs + 1 - i): for k in range(derivs + 1 - i - j): @@ -356,8 +391,10 @@ def d_index(p: int, q: int, r: int) -> int: p01 = orthogonal_basis_triangle(order, derivs, [variables[0], variables[1]]) p2 = orthogonal_basis_interval(order, derivs, [variables[2]]) - poly = [[ScalarFunction(0) for i in range((order + 1) * (order + 1) * (order + 2) // 2)] - for d in range((derivs + 1) * (derivs + 2) * (derivs + 3) // 6)] + poly = [ + [ScalarFunction(0) for i in range((order + 1) * (order + 1) * (order + 2) // 2)] + for d in range((derivs + 1) * (derivs + 2) * (derivs + 3) // 6) + ] for i in range(derivs + 1): for j in range(derivs + 1 - i): for k in range(derivs + 1 - i - j): @@ -382,7 +419,7 @@ def orthogonal_basis_pyramid( def index(i: int, j: int, k: int) -> int: """Get the index.""" - out = k + j * (order + 1) + i * (order + 1) * (order + 2) // 2 - i * (i ** 2 + 5) // 6 + out = k + j * (order + 1) + i * (order + 1) * (order + 2) // 2 - i * (i**2 + 5) // 6 if i > j: out -= i * (j - 1) else: @@ -400,8 +437,10 @@ def combinations(n: int, k: int) -> int: out *= i return out - poly = [[ScalarFunction(0) for i in range((2 * order + 3) * (order + 2) * (order + 1) // 6)] - for d in range((derivs + 1) * (derivs + 2) * (derivs + 3) // 6)] + poly = [ + [ScalarFunction(0) for i in range((2 * order + 3) * (order + 2) * (order + 1) // 6)] + for d in range((derivs + 1) * (derivs + 2) * (derivs + 3) // 6) + ] for dx in range(derivs + 1): for dy in range(derivs + 1 - dx): for dz in range(derivs + 1 - dx - dy): @@ -411,32 +450,48 @@ def combinations(n: int, k: int) -> int: if i > 0: poly[d_index(dx, dy, dz)][index(i, 0, 0)] = ( poly[d_index(dx, dy, dz)][index(i - 1, 0, 0)] - * (2 * variables[0] + variables[2] - 1) * (2 * i - 1) / i + * (2 * variables[0] + variables[2] - 1) + * (2 * i - 1) + / i ) if dx > 0: poly[d_index(dx, dy, dz)][index(i, 0, 0)] += ( poly[d_index(dx - 1, dy, dz)][index(i - 1, 0, 0)] - * 2 * dx * (2 * i - 1) / i + * 2 + * dx + * (2 * i - 1) + / i ) if dz > 0: poly[d_index(dx, dy, dz)][index(i, 0, 0)] += ( poly[d_index(dx, dy, dz - 1)][index(i - 1, 0, 0)] - * dz * (2 * i - 1) / i + * dz + * (2 * i - 1) + / i ) if i > 1: poly[d_index(dx, dy, dz)][index(i, 0, 0)] -= ( poly[d_index(dx, dy, dz)][index(i - 2, 0, 0)] - * (1 - variables[2]) ** 2 * (i - 1) / i + * (1 - variables[2]) ** 2 + * (i - 1) + / i ) if dz > 0: poly[d_index(dx, dy, dz)][index(i, 0, 0)] += ( poly[d_index(dx, dy, dz - 1)][index(i - 2, 0, 0)] - * 2 * (1 - variables[2]) * dz * (i - 1) / i + * 2 + * (1 - variables[2]) + * dz + * (i - 1) + / i ) if dz > 1: poly[d_index(dx, dy, dz)][index(i, 0, 0)] -= ( poly[d_index(dx, dy, dz - 2)][index(i - 2, 0, 0)] - * dz * (dz - 1) * (i - 1) / i + * dz + * (dz - 1) + * (i - 1) + / i ) for j in range(order + 1): @@ -445,86 +500,114 @@ def combinations(n: int, k: int) -> int: poly[d_index(dx, dy, dz)][index(i, j, 0)] = ( poly[d_index(dx, dy, dz)][index(i, j - 1, 0)] * (2 * variables[1] / (1 - variables[2]) - 1) - * (2 * j - 1) / j + * (2 * j - 1) + / j ) if dy > 0: poly[d_index(dx, dy, dz)][index(i, j, 0)] += ( poly[d_index(dx, dy - 1, dz)][index(i, j - 1, 0)] - * 2 * dy / (1 - variables[2]) - * (2 * j - 1) / j + * 2 + * dy + / (1 - variables[2]) + * (2 * j - 1) + / j ) for di in range(1, dz + 1): poly[d_index(dx, dy, dz)][index(i, j, 0)] += ( poly[d_index(dx, dy, dz - di)][index(i, j - 1, 0)] * combinations(dz, di) - * variables[1] / (1 - variables[2]) ** (di + 1) - * 2 * (2 * j - 1) / j + * variables[1] + / (1 - variables[2]) ** (di + 1) + * 2 + * (2 * j - 1) + / j ) if dy > 0: poly[d_index(dx, dy, dz)][index(i, j, 0)] += ( poly[d_index(dx, dy - 1, dz - di)][index(i, j - 1, 0)] * combinations(dz, di) - * dy / (1 - variables[2]) ** (di + 1) - * 2 * (2 * j - 1) / j + * dy + / (1 - variables[2]) ** (di + 1) + * 2 + * (2 * j - 1) + / j ) else: poly[d_index(dx, dy, dz)][index(i, j, 0)] = ( poly[d_index(dx, dy, dz)][index(i, j - 1, 0)] * (2 * variables[1] + variables[2] - 1) - * (2 * j - 1) / j + * (2 * j - 1) + / j ) if dy > 0: poly[d_index(dx, dy, dz)][index(i, j, 0)] += ( poly[d_index(dx, dy - 1, dz)][index(i, j - 1, 0)] - * 2 * dy * (2 * j - 1) / j + * 2 + * dy + * (2 * j - 1) + / j ) if dz > 0: poly[d_index(dx, dy, dz)][index(i, j, 0)] += ( poly[d_index(dx, dy, dz - 1)][index(i, j - 1, 0)] - * dz * (2 * j - 1) / j + * dz + * (2 * j - 1) + / j ) if j > 1: if i >= j: poly[d_index(dx, dy, dz)][index(i, j, 0)] -= ( - poly[d_index(dx, dy, dz)][index(i, j - 2, 0)] - * (j - 1) / j + poly[d_index(dx, dy, dz)][index(i, j - 2, 0)] * (j - 1) / j ) elif i + 1 == j: poly[d_index(dx, dy, dz)][index(i, j, 0)] -= ( poly[d_index(dx, dy, dz)][index(i, j - 2, 0)] - * (1 - variables[2]) * (j - 1) / j + * (1 - variables[2]) + * (j - 1) + / j ) if dz > 0: poly[d_index(dx, dy, dz)][index(i, j, 0)] += ( poly[d_index(dx, dy, dz - 1)][index(i, j - 2, 0)] - * dz * (j - 1) / j + * dz + * (j - 1) + / j ) else: poly[d_index(dx, dy, dz)][index(i, j, 0)] -= ( poly[d_index(dx, dy, dz)][index(i, j - 2, 0)] - * (1 - variables[2]) ** 2 * (j - 1) / j + * (1 - variables[2]) ** 2 + * (j - 1) + / j ) if dz > 0: poly[d_index(dx, dy, dz)][index(i, j, 0)] += ( poly[d_index(dx, dy, dz - 1)][index(i, j - 2, 0)] - * 2 * dz * (1 - variables[2]) * (j - 1) / j + * 2 + * dz + * (1 - variables[2]) + * (j - 1) + / j ) if dz > 1: poly[d_index(dx, dy, dz)][index(i, j, 0)] -= ( poly[d_index(dx, dy, dz - 2)][index(i, j - 2, 0)] - * dz * (dz - 1) * (j - 1) / j + * dz + * (dz - 1) + * (j - 1) + / j ) for k in range(1, order + 1 - max(i, j)): a, b, c = _jrc(2 * max(i, j) + 2, k - 1) poly[d_index(dx, dy, dz)][index(i, j, k)] = ( poly[d_index(dx, dy, dz)][index(i, j, k - 1)] - * a * (2 * variables[2] - 1) + * a + * (2 * variables[2] - 1) ) if dz > 0: poly[d_index(dx, dy, dz)][index(i, j, k)] += ( - poly[d_index(dx, dy, dz - 1)][index(i, j, k - 1)] - * a * 2 * dz + poly[d_index(dx, dy, dz - 1)][index(i, j, k - 1)] * a * 2 * dz ) poly[d_index(dx, dy, dz)][index(i, j, k)] += ( b * poly[d_index(dx, dy, dz)][index(i, j, k - 1)] @@ -593,7 +676,7 @@ def orthonormal_basis( ref = create_reference(cell) if variables is None: variables = x - norms = [sympy.sqrt((f ** 2).integral(ref, dummy_vars=variables)) for f in poly[0]] + norms = [sympy.sqrt((f**2).integral(ref, dummy_vars=variables)) for f in poly[0]] for i, n in enumerate(norms): for j in range(len(poly)): poly[j][i] /= n diff --git a/symfem/polynomials/lobatto.py b/symfem/polynomials/lobatto.py index d0b882ba..e6ff5253 100644 --- a/symfem/polynomials/lobatto.py +++ b/symfem/polynomials/lobatto.py @@ -59,9 +59,13 @@ def lobatto_basis( return [i * j.subs(x[0], x[1]) for i in interval for j in interval] if cell == "hexahedron": interval = lobatto_basis("interval", order, include_endpoints) - return [i * j.subs(x[0], x[1]) * k.subs(x[0], x[2]) - for i in interval for j in interval for k in interval] - raise NotImplementedError(f"Lobatto polynomials not implemented for cell \"{cell}\"") + return [ + i * j.subs(x[0], x[1]) * k.subs(x[0], x[2]) + for i in interval + for j in interval + for k in interval + ] + raise NotImplementedError(f'Lobatto polynomials not implemented for cell "{cell}"') def lobatto_dual_basis( @@ -87,6 +91,10 @@ def lobatto_dual_basis( return [i * j.subs(x[0], x[1]) for i in interval for j in interval] if cell == "hexahedron": interval = lobatto_dual_basis("interval", order, include_endpoints) - return [i * j.subs(x[0], x[1]) * k.subs(x[0], x[2]) - for i in interval for j in interval for k in interval] - raise NotImplementedError(f"Lobatto polynomials not implemented for cell \"{cell}\"") + return [ + i * j.subs(x[0], x[1]) * k.subs(x[0], x[2]) + for i in interval + for j in interval + for k in interval + ] + raise NotImplementedError(f'Lobatto polynomials not implemented for cell "{cell}"') diff --git a/symfem/polynomials/polysets.py b/symfem/polynomials/polysets.py index c0d831d8..0bce50c8 100644 --- a/symfem/polynomials/polysets.py +++ b/symfem/polynomials/polysets.py @@ -76,10 +76,15 @@ def Hdiv_polynomials( """ assert domain_dim == range_dim if domain_dim == 2: - return [VectorFunction(( - variables[0] * variables[0] ** (order - 1 - j) * variables[1] ** j, - variables[1] * variables[0] ** (order - 1 - j) * variables[1] ** j, - )) for j in range(order)] + return [ + VectorFunction( + ( + variables[0] * variables[0] ** (order - 1 - j) * variables[1] ** j, + variables[1] * variables[0] ** (order - 1 - j) * variables[1] ** j, + ) + ) + for j in range(order) + ] if domain_dim == 3: basis: typing.List[VectorFunction] = [] for j in range(order): @@ -107,24 +112,53 @@ def Hcurl_polynomials( """ assert domain_dim == range_dim if domain_dim == 2: - return [VectorFunction(( - variables[0] ** (order - 1 - j) * variables[1] ** (j + 1), - -variables[0] ** (order - j) * variables[1] ** j, - )) for j in range(order)] + return [ + VectorFunction( + ( + variables[0] ** (order - 1 - j) * variables[1] ** (j + 1), + -(variables[0] ** (order - j)) * variables[1] ** j, + ) + ) + for j in range(order) + ] if domain_dim == 3: poly: typing.List[VectorFunction] = [] - poly += [VectorFunction(( - variables[0] ** (m - 1) * variables[1] ** n * variables[2] ** (order - m - n + 1), - 0, -variables[0] ** m * variables[1] ** n * variables[2] ** (order - m - n) - )) for n in range(order) for m in range(1, order + 1 - n)] - poly += [VectorFunction(( - 0, variables[0] ** m * variables[1] ** (n - 1) * variables[2] ** (order - m - n + 1), - -variables[0] ** m * variables[1] ** n * variables[2] ** (order - m - n) - )) for m in range(order) for n in range(1, order + 1 - m)] - poly += [VectorFunction(( - variables[0] ** (order - n) * variables[1] ** n, - -variables[0] ** (order + 1 - n) * variables[1] ** (n - 1), 0 - )) for n in range(1, order + 1)] + poly += [ + VectorFunction( + ( + variables[0] ** (m - 1) + * variables[1] ** n + * variables[2] ** (order - m - n + 1), + 0, + -(variables[0] ** m) * variables[1] ** n * variables[2] ** (order - m - n), + ) + ) + for n in range(order) + for m in range(1, order + 1 - n) + ] + poly += [ + VectorFunction( + ( + 0, + variables[0] ** m + * variables[1] ** (n - 1) + * variables[2] ** (order - m - n + 1), + -(variables[0] ** m) * variables[1] ** n * variables[2] ** (order - m - n), + ) + ) + for m in range(order) + for n in range(1, order + 1 - m) + ] + poly += [ + VectorFunction( + ( + variables[0] ** (order - n) * variables[1] ** n, + -(variables[0] ** (order + 1 - n)) * variables[1] ** (n - 1), + 0, + ) + ) + for n in range(1, order + 1) + ] return poly raise ValueError(f"Unsupported dimension: {domain_dim}") @@ -146,7 +180,7 @@ def quolynomial_set_1d( for j in product(range(order + 1), repeat=dim): poly = ScalarFunction(1) for a, b in zip(variables, j): - poly *= a ** b + poly *= a**b basis.append(poly) return basis @@ -193,7 +227,7 @@ def Hdiv_quolynomials( for j in product(range(order), repeat=domain_dim - 1): poly = 1 for a, b in zip(variables, j[:d] + (order,) + j[d:]): - poly *= a ** b + poly *= a**b basis.append(VectorFunction([poly if i == d else 0 for i in range(domain_dim)])) return basis @@ -222,7 +256,7 @@ def Hcurl_quolynomials( continue poly = 1 for a, b in zip(variables, j): - poly *= a ** b + poly *= a**b basis.append(VectorFunction([poly if i == d else 0 for i in range(domain_dim)])) return basis @@ -272,7 +306,7 @@ def serendipity_set_1d( for i in serendipity_indices(s, s - order, dim): p = 1 for j, k in zip(variables, i): - p *= j ** k + p *= j**k basis.append(ScalarFunction(p)) return basis @@ -316,10 +350,12 @@ def Hdiv_serendipity( assert domain_dim == range_dim if domain_dim == 2: return [ - VectorFunction((variables[0] ** (order + 1), - (order + 1) * variables[0] ** order * variables[1])), - VectorFunction(((order + 1) * variables[0] * variables[1] ** order, - variables[1] ** (order + 1))), + VectorFunction( + (variables[0] ** (order + 1), (order + 1) * variables[0] ** order * variables[1]) + ), + VectorFunction( + ((order + 1) * variables[0] * variables[1] ** order, variables[1] ** (order + 1)) + ), ] if domain_dim == 3: a = [] @@ -329,16 +365,25 @@ def Hdiv_serendipity( else: for i in range(order + 1): p = variables[1] ** i * variables[2] ** (order - i) - a.append(VectorFunction((0, variables[0] * variables[2] * p, - -variables[0] * variables[1] * p))) + a.append( + VectorFunction( + (0, variables[0] * variables[2] * p, -variables[0] * variables[1] * p) + ) + ) p = variables[0] ** i * variables[2] ** (order - i) - a.append(VectorFunction((variables[1] * variables[2] * p, 0, - -variables[0] * variables[1] * p))) + a.append( + VectorFunction( + (variables[1] * variables[2] * p, 0, -variables[0] * variables[1] * p) + ) + ) p = variables[0] ** i * variables[1] ** (order - i) - a.append(VectorFunction((variables[1] * variables[2] * p, - -variables[0] * variables[2] * p, 0))) + a.append( + VectorFunction( + (variables[1] * variables[2] * p, -variables[0] * variables[2] * p, 0) + ) + ) return [i.curl() for i in a] @@ -362,10 +407,12 @@ def Hcurl_serendipity( assert domain_dim == range_dim if domain_dim == 2: return [ - VectorFunction(((order + 1) * variables[0] ** order * variables[1], - -variables[0] ** (order + 1))), - VectorFunction((variables[1] ** (order + 1), - (order + 1) * variables[0] * variables[1] ** order)), + VectorFunction( + ((order + 1) * variables[0] ** order * variables[1], -(variables[0] ** (order + 1))) + ), + VectorFunction( + (variables[1] ** (order + 1), (order + 1) * variables[0] * variables[1] ** order) + ), ] if domain_dim == 3: out: typing.List[VectorFunction] = [] @@ -377,16 +424,25 @@ def Hcurl_serendipity( else: for i in range(order): p = variables[0] ** i * variables[2] ** (order - 1 - i) - out.append(VectorFunction((variables[1] * variables[2] * p, 0, - -variables[0] * variables[1] * p))) + out.append( + VectorFunction( + (variables[1] * variables[2] * p, 0, -variables[0] * variables[1] * p) + ) + ) p = variables[1] ** i * variables[2] ** (order - 1 - i) - out.append(VectorFunction((0, variables[0] * variables[2] * p, - -variables[0] * variables[1] * p))) + out.append( + VectorFunction( + (0, variables[0] * variables[2] * p, -variables[0] * variables[1] * p) + ) + ) p = variables[0] ** i * variables[1] ** (order - 1 - i) - out.append(VectorFunction((variables[1] * variables[2] * p, - -variables[0] * variables[2] * p, 0))) + out.append( + VectorFunction( + (variables[1] * variables[2] * p, -variables[0] * variables[2] * p, 0) + ) + ) for p in serendipity_set_1d(domain_dim, order + 1): out.append(VectorFunction(tuple(p.diff(i) for i in variables))) diff --git a/symfem/quadrature.py b/symfem/quadrature.py index 18b9ec88..08ca43ca 100644 --- a/symfem/quadrature.py +++ b/symfem/quadrature.py @@ -7,9 +7,7 @@ Scalar = typing.Union[sympy.core.expr.Expr, int] -def equispaced( - n: int -) -> typing.Tuple[typing.List[Scalar], typing.List[Scalar]]: +def equispaced(n: int) -> typing.Tuple[typing.List[Scalar], typing.List[Scalar]]: """Get equispaced points and weights. Args: @@ -18,14 +16,16 @@ def equispaced( Returns: Quadrature points and weights """ - return ([sympy.Rational(i, n - 1) for i in range(n)], - [sympy.Rational(1, 2*(n-1)) if i == 0 or i == n - 1 else sympy.Rational(1, n-1) - for i in range(n)]) + return ( + [sympy.Rational(i, n - 1) for i in range(n)], + [ + sympy.Rational(1, 2 * (n - 1)) if i == 0 or i == n - 1 else sympy.Rational(1, n - 1) + for i in range(n) + ], + ) -def lobatto( - n: int -) -> typing.Tuple[typing.List[Scalar], typing.List[Scalar]]: +def lobatto(n: int) -> typing.Tuple[typing.List[Scalar], typing.List[Scalar]]: """Get Gauss-Lobatto-Legendre points and weights. Args: @@ -35,50 +35,83 @@ def lobatto( Quadrature points and weights """ if n == 2: - return ([0, 1], - [sympy.Rational(1, 2), sympy.Rational(1, 2)]) + return ([0, 1], [sympy.Rational(1, 2), sympy.Rational(1, 2)]) if n == 3: - return ([0, sympy.Rational(1, 2), 1], - [sympy.Rational(1, 6), sympy.Rational(2, 3), sympy.Rational(1, 6)]) + return ( + [0, sympy.Rational(1, 2), 1], + [sympy.Rational(1, 6), sympy.Rational(2, 3), sympy.Rational(1, 6)], + ) if n == 4: - return ([0, (1 - 1 / sympy.sqrt(5)) / 2, (1 + 1 / sympy.sqrt(5)) / 2, 1], - [sympy.Rational(1, 12), sympy.Rational(5, 12), sympy.Rational(5, 12), - sympy.Rational(1, 12)]) + return ( + [0, (1 - 1 / sympy.sqrt(5)) / 2, (1 + 1 / sympy.sqrt(5)) / 2, 1], + [ + sympy.Rational(1, 12), + sympy.Rational(5, 12), + sympy.Rational(5, 12), + sympy.Rational(1, 12), + ], + ) if n == 5: - return ([0, (1 - sympy.sqrt(3) / sympy.sqrt(7)) / 2, sympy.Rational(1, 2), - (1 + sympy.sqrt(3) / sympy.sqrt(7)) / 2, 1], - [sympy.Rational(1, 20), sympy.Rational(49, 180), sympy.Rational(16, 45), - sympy.Rational(49, 180), sympy.Rational(1, 20)]) + return ( + [ + 0, + (1 - sympy.sqrt(3) / sympy.sqrt(7)) / 2, + sympy.Rational(1, 2), + (1 + sympy.sqrt(3) / sympy.sqrt(7)) / 2, + 1, + ], + [ + sympy.Rational(1, 20), + sympy.Rational(49, 180), + sympy.Rational(16, 45), + sympy.Rational(49, 180), + sympy.Rational(1, 20), + ], + ) if n == 6: - return ([0, - (1 - sympy.sqrt(sympy.Rational(1, 3) + (2 * sympy.sqrt(7) / 21))) / 2, - (1 - sympy.sqrt(sympy.Rational(1, 3) - (2 * sympy.sqrt(7) / 21))) / 2, - (1 + sympy.sqrt(sympy.Rational(1, 3) - (2 * sympy.sqrt(7) / 21))) / 2, - (1 + sympy.sqrt(sympy.Rational(1, 3) + (2 * sympy.sqrt(7) / 21))) / 2, - 1], - [sympy.Rational(1, 30), (14 - sympy.sqrt(7)) / 60, (14 + sympy.sqrt(7)) / 60, - (14 + sympy.sqrt(7)) / 60, (14 - sympy.sqrt(7)) / 60, sympy.Rational(1, 30)]) + return ( + [ + 0, + (1 - sympy.sqrt(sympy.Rational(1, 3) + (2 * sympy.sqrt(7) / 21))) / 2, + (1 - sympy.sqrt(sympy.Rational(1, 3) - (2 * sympy.sqrt(7) / 21))) / 2, + (1 + sympy.sqrt(sympy.Rational(1, 3) - (2 * sympy.sqrt(7) / 21))) / 2, + (1 + sympy.sqrt(sympy.Rational(1, 3) + (2 * sympy.sqrt(7) / 21))) / 2, + 1, + ], + [ + sympy.Rational(1, 30), + (14 - sympy.sqrt(7)) / 60, + (14 + sympy.sqrt(7)) / 60, + (14 + sympy.sqrt(7)) / 60, + (14 - sympy.sqrt(7)) / 60, + sympy.Rational(1, 30), + ], + ) if n == 7: - return ([0, - (1 - sympy.sqrt((5 + 2 * sympy.sqrt(5) / sympy.sqrt(3)) / 11)) / 2, - (1 - sympy.sqrt((5 - 2 * sympy.sqrt(5) / sympy.sqrt(3)) / 11)) / 2, - sympy.Rational(1, 2), - (1 + sympy.sqrt((5 - 2 * sympy.sqrt(5) / sympy.sqrt(3)) / 11)) / 2, - (1 + sympy.sqrt((5 + 2 * sympy.sqrt(5) / sympy.sqrt(3)) / 11)) / 2, - 1], - [sympy.Rational(1, 42), - (124 - 7 * sympy.sqrt(15)) / 700, - (124 + 7 * sympy.sqrt(15)) / 700, - sympy.Rational(128, 525), - (124 + 7 * sympy.sqrt(15)) / 700, - (124 - 7 * sympy.sqrt(15)) / 700, - sympy.Rational(1, 42)]) + return ( + [ + 0, + (1 - sympy.sqrt((5 + 2 * sympy.sqrt(5) / sympy.sqrt(3)) / 11)) / 2, + (1 - sympy.sqrt((5 - 2 * sympy.sqrt(5) / sympy.sqrt(3)) / 11)) / 2, + sympy.Rational(1, 2), + (1 + sympy.sqrt((5 - 2 * sympy.sqrt(5) / sympy.sqrt(3)) / 11)) / 2, + (1 + sympy.sqrt((5 + 2 * sympy.sqrt(5) / sympy.sqrt(3)) / 11)) / 2, + 1, + ], + [ + sympy.Rational(1, 42), + (124 - 7 * sympy.sqrt(15)) / 700, + (124 + 7 * sympy.sqrt(15)) / 700, + sympy.Rational(128, 525), + (124 + 7 * sympy.sqrt(15)) / 700, + (124 - 7 * sympy.sqrt(15)) / 700, + sympy.Rational(1, 42), + ], + ) raise NotImplementedError() -def radau( - n: int -) -> typing.Tuple[typing.List[Scalar], typing.List[Scalar]]: +def radau(n: int) -> typing.Tuple[typing.List[Scalar], typing.List[Scalar]]: """Get Radau points and weights. Args: @@ -88,17 +121,16 @@ def radau( Quadrature points and weights """ if n == 2: - return ([0, sympy.Rational(2, 3)], - [sympy.Rational(1, 4), sympy.Rational(3, 4)]) + return ([0, sympy.Rational(2, 3)], [sympy.Rational(1, 4), sympy.Rational(3, 4)]) if n == 3: - return ([0, (6 - sympy.sqrt(6)) / 10, (6 + sympy.sqrt(6)) / 10], - [sympy.Rational(1, 9), (16 + sympy.sqrt(6)) / 36, (16 - sympy.sqrt(6)) / 36]) + return ( + [0, (6 - sympy.sqrt(6)) / 10, (6 + sympy.sqrt(6)) / 10], + [sympy.Rational(1, 9), (16 + sympy.sqrt(6)) / 36, (16 - sympy.sqrt(6)) / 36], + ) raise NotImplementedError() -def legendre( - n: int -) -> typing.Tuple[typing.List[Scalar], typing.List[Scalar]]: +def legendre(n: int) -> typing.Tuple[typing.List[Scalar], typing.List[Scalar]]: """Get Gauss-Legendre points and weights. Args: @@ -110,17 +142,19 @@ def legendre( if n == 1: return ([sympy.Rational(1, 2)], [1]) if n == 2: - return ([(3 - sympy.sqrt(3)) / 6, (3 + sympy.sqrt(3)) / 6], - [sympy.Rational(1, 2), sympy.Rational(1, 2)]) + return ( + [(3 - sympy.sqrt(3)) / 6, (3 + sympy.sqrt(3)) / 6], + [sympy.Rational(1, 2), sympy.Rational(1, 2)], + ) if n == 3: - return ([(5 - sympy.sqrt(15)) / 10, sympy.Rational(1, 2), (5 + sympy.sqrt(15)) / 10], - [sympy.Rational(5, 18), sympy.Rational(4, 9), sympy.Rational(5, 18)]) + return ( + [(5 - sympy.sqrt(15)) / 10, sympy.Rational(1, 2), (5 + sympy.sqrt(15)) / 10], + [sympy.Rational(5, 18), sympy.Rational(4, 9), sympy.Rational(5, 18)], + ) raise NotImplementedError() -def get_quadrature( - rule: str, n: int -) -> typing.Tuple[typing.List[Scalar], typing.List[Scalar]]: +def get_quadrature(rule: str, n: int) -> typing.Tuple[typing.List[Scalar], typing.List[Scalar]]: """Get quadrature points and weights. Args: diff --git a/symfem/references.py b/symfem/references.py index 67c38421..5f591df0 100644 --- a/symfem/references.py +++ b/symfem/references.py @@ -9,14 +9,23 @@ import symfem.functions -from .geometry import (PointType, PointTypeInput, SetOfPoints, SetOfPointsInput, parse_point_input, - parse_set_of_points_input) -from .symbols import AxisVariablesNotSingle, t, x +from symfem.geometry import ( + PointType, + PointTypeInput, + SetOfPoints, + SetOfPointsInput, + parse_point_input, + parse_set_of_points_input, +) +from symfem.symbols import AxisVariablesNotSingle, t, x LatticeWithLines = typing.Tuple[SetOfPoints, typing.List[typing.Tuple[int, int]]] -IntLimits = typing.List[typing.Union[ - typing.Tuple[sympy.core.symbol.Symbol, sympy.core.expr.Expr, sympy.core.expr.Expr], - typing.Tuple[sympy.core.symbol.Symbol, sympy.core.expr.Expr]]] +IntLimits = typing.List[ + typing.Union[ + typing.Tuple[sympy.core.symbol.Symbol, sympy.core.expr.Expr, sympy.core.expr.Expr], + typing.Tuple[sympy.core.symbol.Symbol, sympy.core.expr.Expr], + ] +] class NonDefaultReferenceError(NotImplementedError): @@ -167,13 +176,19 @@ def __init__(self, vertices: SetOfPointsInput = ()): """ def __build__( - self, tdim: int, name: str, origin: PointTypeInput, axes: SetOfPointsInput, - reference_vertices: SetOfPointsInput, vertices: SetOfPointsInput, + self, + tdim: int, + name: str, + origin: PointTypeInput, + axes: SetOfPointsInput, + reference_vertices: SetOfPointsInput, + vertices: SetOfPointsInput, edges: typing.Tuple[typing.Tuple[int, int], ...], faces: typing.Tuple[typing.Tuple[int, ...], ...], volumes: typing.Tuple[typing.Tuple[int, ...], ...], sub_entity_types: typing.List[typing.Union[typing.List[str], str, None]], - simplex: bool = False, tp: bool = False + simplex: bool = False, + tp: bool = False, ): """Create a reference cell. @@ -445,7 +460,8 @@ def jacobian(self) -> sympy.core.expr.Expr: Returns: The Jacobian """ - from .functions import VectorFunction + from symfem.functions import VectorFunction + assert len(self.axes) == self.tdim vaxes = [VectorFunction(a) for a in self.axes] if self.tdim == 1: @@ -474,7 +490,7 @@ def tangent(self) -> PointType: The tangent """ if self.tdim == 1: - norm = sympy.sqrt(sum(i ** 2 for i in self.axes[0])) + norm = sympy.sqrt(sum(i**2 for i in self.axes[0])) return _vnormalise(tuple(i / norm for i in self.axes[0])) raise RuntimeError @@ -511,7 +527,7 @@ def sub_entities( assert codim is not None dim = self.tdim - codim if dim == 0: - return tuple((i, ) for i, _ in enumerate(self.vertices)) + return tuple((i,) for i, _ in enumerate(self.vertices)) if dim == 1: return self.edges if dim == 2: @@ -555,13 +571,15 @@ def sub_entity(self, dim: int, n: int, reference_vertices: bool = False) -> typi if reference_vertices: return create_reference( - entity_type, tuple(self.reference_vertices[i] for i in self.sub_entities(dim)[n])) + entity_type, tuple(self.reference_vertices[i] for i in self.sub_entities(dim)[n]) + ) else: if self.tdim == dim: return self else: return create_reference( - entity_type, tuple(self.vertices[i] for i in self.sub_entities(dim)[n])) + entity_type, tuple(self.vertices[i] for i in self.sub_entities(dim)[n]) + ) def at_vertex(self, point: PointType) -> bool: """Check if a point is a vertex of the reference. @@ -586,7 +604,8 @@ def on_edge(self, point_in: PointType) -> bool: Returns: Is the point on an edge? """ - from .functions import VectorFunction + from symfem.functions import VectorFunction + point = VectorFunction(point_in) for e in self.edges: v0 = VectorFunction(self.vertices[e[0]]) @@ -605,7 +624,8 @@ def on_face(self, point_in: PointType) -> bool: Returns: Is the point on a face? """ - from .functions import VectorFunction + from symfem.functions import VectorFunction + point = VectorFunction(point_in) for f in self.faces: v0 = VectorFunction(self.vertices[f[0]]) @@ -631,7 +651,7 @@ def map_polyset_from_default( self, poly: typing.List[symfem.functions.FunctionInput] ) -> typing.List[symfem.functions.FunctionInput]: """Map the polynomials from the default reference element to this reference.""" - from .functions import parse_function_input + from symfem.functions import parse_function_input if self == self.default_reference(): return poly @@ -639,17 +659,20 @@ def map_polyset_from_default( return [parse_function_input(p).subs(x, invmap) for p in poly] def plot_entity_diagrams( - self, filename: typing.Union[str, typing.List[str]], - plot_options: typing.Dict[str, typing.Any] = {}, **kwargs: typing.Any + self, + filename: typing.Union[str, typing.List[str]], + plot_options: typing.Dict[str, typing.Any] = {}, + **kwargs: typing.Any, ): """Plot diagrams showing the entity numbering of the reference.""" - from .plotting import Picture, colors + from symfem.plotting import Picture, colors img = Picture(**kwargs) if self.tdim == 1: - offset_unit: typing.Tuple[typing.Union[ - sympy.core.expr.Expr, int], ...] = (sympy.Rational(4, 3), ) + offset_unit: typing.Tuple[typing.Union[sympy.core.expr.Expr, int], ...] = ( + sympy.Rational(4, 3), + ) img.add_arrow((-sympy.Rational(1, 2), 0), (-sympy.Rational(1, 3), 0)) img.add_math((-sympy.Rational(8, 25), 0), "x", anchor="west") elif self.tdim == 2: @@ -657,16 +680,20 @@ def plot_entity_diagrams( offset_unit = (sympy.Rational(9, 4), 0) rt52 = sympy.sqrt(3) / 4 img.add_arrow((-sympy.Rational(3, 4), -rt52), (-sympy.Rational(7, 12), -rt52)) - img.add_arrow((-sympy.Rational(3, 4), -rt52), - (-sympy.Rational(3, 4), sympy.Rational(1, 6) - rt52)) + img.add_arrow( + (-sympy.Rational(3, 4), -rt52), + (-sympy.Rational(3, 4), sympy.Rational(1, 6) - rt52), + ) img.add_math((-sympy.Rational(171, 300), -rt52), "x", anchor="west") - img.add_math((-sympy.Rational(3, 4), sympy.Rational(9, 50) - rt52), "y", - anchor="south") + img.add_math( + (-sympy.Rational(3, 4), sympy.Rational(9, 50) - rt52), "y", anchor="south" + ) else: offset_unit = (sympy.Rational(4, 3), 0) img.add_arrow((-sympy.Rational(1, 2), 0), (-sympy.Rational(1, 3), 0)) - img.add_arrow((-sympy.Rational(1, 2), 0), - (-sympy.Rational(1, 2), sympy.Rational(1, 6))) + img.add_arrow( + (-sympy.Rational(1, 2), 0), (-sympy.Rational(1, 2), sympy.Rational(1, 6)) + ) img.add_math((-sympy.Rational(8, 25), 0), "x", anchor="west") img.add_math((-sympy.Rational(1, 2), sympy.Rational(9, 50)), "y", anchor="south") elif self.tdim == 3: @@ -676,17 +703,27 @@ def plot_entity_diagrams( offset_unit = (sympy.Rational(3, 2), sympy.Rational(9, 15), 0) else: offset_unit = (1, sympy.Rational(2, 5), 0) - img.add_arrow((-sympy.Rational(3, 8), -sympy.Rational(3, 20), 0), - (-sympy.Rational(5, 24), -sympy.Rational(3, 20), 0)) - img.add_arrow((-sympy.Rational(3, 8), -sympy.Rational(3, 20), 0), - (-sympy.Rational(3, 8), sympy.Rational(1, 60), 0)) - img.add_arrow((-sympy.Rational(3, 8), -sympy.Rational(3, 20), 0), - (-sympy.Rational(3, 8), -sympy.Rational(3, 20), sympy.Rational(1, 6))) + img.add_arrow( + (-sympy.Rational(3, 8), -sympy.Rational(3, 20), 0), + (-sympy.Rational(5, 24), -sympy.Rational(3, 20), 0), + ) + img.add_arrow( + (-sympy.Rational(3, 8), -sympy.Rational(3, 20), 0), + (-sympy.Rational(3, 8), sympy.Rational(1, 60), 0), + ) + img.add_arrow( + (-sympy.Rational(3, 8), -sympy.Rational(3, 20), 0), + (-sympy.Rational(3, 8), -sympy.Rational(3, 20), sympy.Rational(1, 6)), + ) img.add_math((-sympy.Rational(39, 200), -sympy.Rational(3, 20), 0), "x", anchor="west") - img.add_math((-sympy.Rational(3, 8), sympy.Rational(1, 60), 0), "y", - anchor="south west") - img.add_math((-sympy.Rational(3, 8), -sympy.Rational(3, 20), sympy.Rational(9, 50)), - "z", anchor="south") + img.add_math( + (-sympy.Rational(3, 8), sympy.Rational(1, 60), 0), "y", anchor="south west" + ) + img.add_math( + (-sympy.Rational(3, 8), -sympy.Rational(3, 20), sympy.Rational(9, 50)), + "z", + anchor="south", + ) else: raise ValueError("Unsupported tdim") @@ -710,7 +747,7 @@ def offset(point, cd): class Point(Reference): """A point.""" - def __init__(self, vertices: SetOfPointsInput = ((), )): + def __init__(self, vertices: SetOfPointsInput = ((),)): """Create a point. Args: @@ -722,13 +759,15 @@ def __init__(self, vertices: SetOfPointsInput = ((), )): name="point", origin=vertices[0], axes=(), - reference_vertices=((), ), + reference_vertices=((),), vertices=vertices, edges=(), faces=(), volumes=(), sub_entity_types=["point", None, None, None], - simplex=True, tp=True) + simplex=True, + tp=True, + ) def default_reference(self) -> Reference: """Get the default reference for this cell type. @@ -855,7 +894,9 @@ def __init__(self, vertices: SetOfPointsInput = ((0,), (1,))): faces=(), volumes=(), sub_entity_types=["point", "interval", None, None], - simplex=True, tp=True) + simplex=True, + tp=True, + ) def default_reference(self) -> Reference: """Get the default reference for this cell type. @@ -874,8 +915,12 @@ def make_lattice(self, n: int) -> SetOfPoints: Returns: A lattice of points offset from the edge of the cell """ - return tuple(tuple(a + (b - a) * sympy.Rational(2 * i + 1, 2 * (n + 1)) - for a, b in zip(*self.vertices)) for i in range(n)) + return tuple( + tuple( + a + (b - a) * sympy.Rational(2 * i + 1, 2 * (n + 1)) for a, b in zip(*self.vertices) + ) + for i in range(n) + ) def make_lattice_with_lines(self, n: int) -> LatticeWithLines: """Make a lattice of points, and a list of lines connecting them. @@ -887,8 +932,10 @@ def make_lattice_with_lines(self, n: int) -> LatticeWithLines: A lattice of points including the edges of the cell Pairs of point numbers that make a mesh of lines across the cell """ - pts = tuple(tuple(a + (b - a) * sympy.Rational(i, n - 1) - for a, b in zip(*self.vertices)) for i in range(n)) + pts = tuple( + tuple(a + (b - a) * sympy.Rational(i, n - 1) for a, b in zip(*self.vertices)) + for i in range(n) + ) pairs = [(i, i + 1) for i in range(n - 1)] return pts, pairs @@ -928,7 +975,7 @@ def get_inverse_map_to(self, vertices_in: SetOfPointsInput) -> PointType: vertices = parse_set_of_points_input(vertices_in) p = _vsub(tuple(x), vertices[0]) v = _vsub(vertices[1], vertices[0]) - return (_vdot(p, v) * sympy.Integer(1) / _vdot(v, v), ) + return (_vdot(p, v) * sympy.Integer(1) / _vdot(v, v),) def _compute_map_to_self(self) -> PointType: """Compute the map from the canonical reference to this reference. @@ -946,7 +993,7 @@ def _compute_inverse_map_to_self(self) -> PointType: """ p = _vsub(tuple(x), self.vertices[0]) v = _vsub(self.vertices[1], self.vertices[0]) - return (_vdot(p, v) * sympy.Integer(1) / _vdot(v, v), ) + return (_vdot(p, v) * sympy.Integer(1) / _vdot(v, v),) def volume(self) -> sympy.core.expr.Expr: """Calculate the volume. @@ -991,7 +1038,8 @@ def __init__(self, vertices: SetOfPointsInput = ((0, 0), (1, 0), (0, 1))): faces=((0, 1, 2),), volumes=(), sub_entity_types=["point", "interval", "triangle", None], - simplex=True) + simplex=True, + ) def default_reference(self) -> Reference: """Get the default reference for this cell type. @@ -1010,10 +1058,14 @@ def make_lattice(self, n: int) -> SetOfPoints: Returns: A lattice of points offset from the edge of the cell """ - return tuple(tuple( - o + ((2 * i + 1) * a0 + (2 * j + 1) * a1) / 2 / (n + 1) - for o, a0, a1 in zip(self.origin, *self.axes) - ) for i in range(n) for j in range(n - i)) + return tuple( + tuple( + o + ((2 * i + 1) * a0 + (2 * j + 1) * a1) / 2 / (n + 1) + for o, a0, a1 in zip(self.origin, *self.axes) + ) + for i in range(n) + for j in range(n - i) + ) def make_lattice_with_lines(self, n: int) -> LatticeWithLines: """Make a lattice of points, and a list of lines connecting them. @@ -1025,13 +1077,14 @@ def make_lattice_with_lines(self, n: int) -> LatticeWithLines: A lattice of points including the edges of the cell Pairs of point numbers that make a mesh of lines across the cell """ - pts = tuple(tuple( - o + (i * a0 + j * a1) / (n - 1) - for o, a0, a1 in zip(self.origin, *self.axes) - ) for i in range(n) for j in range(n - i)) + pts = tuple( + tuple(o + (i * a0 + j * a1) / (n - 1) for o, a0, a1 in zip(self.origin, *self.axes)) + for i in range(n) + for j in range(n - i) + ) pairs = [] s = 0 - for j in range(n-1, 0, -1): + for j in range(n - 1, 0, -1): pairs += [(i, i + 1) for i in range(s, s + j)] s += j + 1 for k in range(n + 1): @@ -1061,8 +1114,10 @@ def integration_limits(self, vars: AxisVariablesNotSingle = t) -> IntLimits: Returns: Integration limits that can be passed into sympy.integrate """ - return [(vars[1], sympy.Integer(0), 1 - vars[0]), - (vars[0], sympy.Integer(0), sympy.Integer(1))] + return [ + (vars[1], sympy.Integer(0), 1 - vars[0]), + (vars[0], sympy.Integer(0), sympy.Integer(1)), + ] def get_map_to(self, vertices: SetOfPointsInput) -> PointType: """Get the map from the reference to a cell. @@ -1091,8 +1146,7 @@ def get_inverse_map_to(self, vertices_in: SetOfPointsInput) -> PointType: p = _vsub(tuple(x), vertices[0]) v1 = _vsub(vertices[1], vertices[0]) v2 = _vsub(vertices[2], vertices[0]) - mat = sympy.Matrix([[v1[0], v2[0]], - [v1[1], v2[1]]]).inv() + mat = sympy.Matrix([[v1[0], v2[0]], [v1[1], v2[1]]]).inv() return (_vdot(mat.row(0), p), _vdot(mat.row(1), p)) def _compute_map_to_self(self) -> PointType: @@ -1101,8 +1155,7 @@ def _compute_map_to_self(self) -> PointType: Returns: The map """ - return tuple(v0 + (v1 - v0) * x[0] + (v2 - v0) * x[1] - for v0, v1, v2 in zip(*self.vertices)) + return tuple(v0 + (v1 - v0) * x[0] + (v2 - v0) * x[1] for v0, v1, v2 in zip(*self.vertices)) def _compute_inverse_map_to_self(self) -> PointType: """Compute the inverse map from the canonical reference to this reference. @@ -1114,8 +1167,7 @@ def _compute_inverse_map_to_self(self) -> PointType: p = _vsub(tuple(x), self.vertices[0]) v1 = _vsub(self.vertices[1], self.vertices[0]) v2 = _vsub(self.vertices[2], self.vertices[0]) - mat = sympy.Matrix([[v1[0], v2[0]], - [v1[1], v2[1]]]).inv() + mat = sympy.Matrix([[v1[0], v2[0]], [v1[1], v2[1]]]).inv() return (_vdot(mat.row(0), p), _vdot(mat.row(1), p)) return tuple( @@ -1175,7 +1227,8 @@ def __init__(self, vertices: SetOfPointsInput = ((0, 0, 0), (1, 0, 0), (0, 1, 0) faces=((1, 2, 3), (0, 2, 3), (0, 1, 3), (0, 1, 2)), volumes=((0, 1, 2, 3),), sub_entity_types=["point", "interval", "triangle", "tetrahedron"], - simplex=True) + simplex=True, + ) @property def clockwise_vertices(self) -> SetOfPoints: @@ -1195,7 +1248,7 @@ def z_ordered_entities(self) -> typing.List[typing.List[typing.Tuple[int, int]]] return [ [(2, 0), (2, 1), (2, 3), (1, 0), (1, 2), (1, 4), (0, 2)], [(3, 0)], - [(2, 2), (1, 1), (1, 3), (1, 5), (0, 0), (0, 1), (0, 3)] + [(2, 2), (1, 1), (1, 3), (1, 5), (0, 0), (0, 1), (0, 3)], ] def default_reference(self) -> Reference: @@ -1215,10 +1268,15 @@ def make_lattice(self, n: int) -> SetOfPoints: Returns: A lattice of points offset from the edge of the cell """ - return tuple(tuple( - o + ((2 * i + 1) * a0 + (2 * j + 1) * a1 + (2 * k + 1) * a2) / 2 / (n + 1) - for o, a0, a1, a2 in zip(self.origin, *self.axes) - ) for i in range(n) for j in range(n - i) for k in range(n - i - j)) + return tuple( + tuple( + o + ((2 * i + 1) * a0 + (2 * j + 1) * a1 + (2 * k + 1) * a2) / 2 / (n + 1) + for o, a0, a1, a2 in zip(self.origin, *self.axes) + ) + for i in range(n) + for j in range(n - i) + for k in range(n - i - j) + ) def make_lattice_with_lines(self, n: int) -> LatticeWithLines: """Make a lattice of points, and a list of lines connecting them. @@ -1241,9 +1299,11 @@ def integration_limits(self, vars: AxisVariablesNotSingle = t) -> IntLimits: Returns: Integration limits that can be passed into sympy.integrate """ - return [(vars[0], sympy.Integer(0), 1 - vars[1] - vars[2]), - (vars[1], sympy.Integer(0), 1 - vars[2]), - (vars[2], sympy.Integer(0), sympy.Integer(1))] + return [ + (vars[0], sympy.Integer(0), 1 - vars[1] - vars[2]), + (vars[1], sympy.Integer(0), 1 - vars[2]), + (vars[2], sympy.Integer(0), sympy.Integer(1)), + ] def get_map_to(self, vertices: SetOfPointsInput) -> PointType: """Get the map from the reference to a cell. @@ -1255,8 +1315,10 @@ def get_map_to(self, vertices: SetOfPointsInput) -> PointType: The map """ assert self.vertices == self.reference_vertices - return tuple(v0 + (v1 - v0) * x[0] + (v2 - v0) * x[1] + (v3 - v0) * x[2] - for v0, v1, v2, v3 in zip(*vertices)) + return tuple( + v0 + (v1 - v0) * x[0] + (v2 - v0) * x[1] + (v3 - v0) * x[2] + for v0, v1, v2, v3 in zip(*vertices) + ) def get_inverse_map_to(self, vertices_in: SetOfPointsInput) -> PointType: """Get the inverse map from a cell to the reference. @@ -1274,9 +1336,9 @@ def get_inverse_map_to(self, vertices_in: SetOfPointsInput) -> PointType: v1 = _vsub(vertices[1], vertices[0]) v2 = _vsub(vertices[2], vertices[0]) v3 = _vsub(vertices[3], vertices[0]) - mat = sympy.Matrix([[v1[0], v2[0], v3[0]], - [v1[1], v2[1], v3[1]], - [v1[2], v2[2], v3[2]]]).inv() + mat = sympy.Matrix( + [[v1[0], v2[0], v3[0]], [v1[1], v2[1], v3[1]], [v1[2], v2[2], v3[2]]] + ).inv() return (_vdot(mat.row(0), p), _vdot(mat.row(1), p), _vdot(mat.row(2), p)) def _compute_map_to_self(self) -> PointType: @@ -1285,8 +1347,10 @@ def _compute_map_to_self(self) -> PointType: Returns: The map """ - return tuple(v0 + (v1 - v0) * x[0] + (v2 - v0) * x[1] + (v3 - v0) * x[2] - for v0, v1, v2, v3 in zip(*self.vertices)) + return tuple( + v0 + (v1 - v0) * x[0] + (v2 - v0) * x[1] + (v3 - v0) * x[2] + for v0, v1, v2, v3 in zip(*self.vertices) + ) def _compute_inverse_map_to_self(self) -> PointType: """Compute the inverse map from the canonical reference to this reference. @@ -1298,9 +1362,9 @@ def _compute_inverse_map_to_self(self) -> PointType: v1 = _vsub(self.vertices[1], self.vertices[0]) v2 = _vsub(self.vertices[2], self.vertices[0]) v3 = _vsub(self.vertices[3], self.vertices[0]) - mat = sympy.Matrix([[v1[0], v2[0], v3[0]], - [v1[1], v2[1], v3[1]], - [v1[2], v2[2], v3[2]]]).inv() + mat = sympy.Matrix( + [[v1[0], v2[0], v3[0]], [v1[1], v2[1], v3[1]], [v1[2], v2[2], v3[2]]] + ).inv() return (_vdot(mat.row(0), p), _vdot(mat.row(1), p), _vdot(mat.row(2), p)) def volume(self) -> sympy.core.expr.Expr: @@ -1325,9 +1389,9 @@ def contains(self, point: PointType) -> bool: else: po = _vsub(point, self.origin) minv = sympy.Matrix([[a[i] for a in self.axes] for i in range(3)]).inv() - t0 = (minv[0, 0] * po[0] + minv[0, 1] * po[1] + minv[0, 2] * po[2]) - t1 = (minv[1, 0] * po[0] + minv[1, 1] * po[1] + minv[1, 2] * po[2]) - t2 = (minv[2, 0] * po[0] + minv[2, 1] * po[1] + minv[2, 2] * po[2]) + t0 = minv[0, 0] * po[0] + minv[0, 1] * po[1] + minv[0, 2] * po[2] + t1 = minv[1, 0] * po[0] + minv[1, 1] * po[1] + minv[1, 2] * po[2] + t2 = minv[2, 0] * po[0] + minv[2, 1] * po[1] + minv[2, 2] * po[2] return 0 <= t0 and 0 <= t1 and 0 >= t2 and t0 + t1 + t2 <= 1 @@ -1352,7 +1416,8 @@ def __init__(self, vertices: SetOfPointsInput = ((0, 0), (1, 0), (0, 1), (1, 1)) faces=((0, 1, 2, 3),), volumes=(), sub_entity_types=["point", "interval", "quadrilateral", None], - tp=True) + tp=True, + ) @property def clockwise_vertices(self) -> SetOfPoints: @@ -1380,10 +1445,14 @@ def make_lattice(self, n: int) -> SetOfPoints: Returns: A lattice of points offset from the edge of the cell """ - return tuple(tuple( - o + ((2 * i + 1) * a0 + (2 * j + 1) * a1) / 2 / (n + 1) - for o, a0, a1 in zip(self.origin, *self.axes) - ) for i in range(n + 1) for j in range(n + 1)) + return tuple( + tuple( + o + ((2 * i + 1) * a0 + (2 * j + 1) * a1) / 2 / (n + 1) + for o, a0, a1 in zip(self.origin, *self.axes) + ) + for i in range(n + 1) + for j in range(n + 1) + ) def make_lattice_with_lines(self, n: int) -> LatticeWithLines: """Make a lattice of points, and a list of lines connecting them. @@ -1395,10 +1464,11 @@ def make_lattice_with_lines(self, n: int) -> LatticeWithLines: A lattice of points including the edges of the cell Pairs of point numbers that make a mesh of lines across the cell """ - pts = tuple(tuple( - o + (i * a0 + j * a1) / (n - 1) - for o, a0, a1 in zip(self.origin, *self.axes) - ) for i in range(n) for j in range(n)) + pts = tuple( + tuple(o + (i * a0 + j * a1) / (n - 1) for o, a0, a1 in zip(self.origin, *self.axes)) + for i in range(n) + for j in range(n) + ) pairs = [] for i in range(n): for j in range(n): @@ -1428,8 +1498,10 @@ def integration_limits(self, vars: AxisVariablesNotSingle = t) -> IntLimits: Returns: Integration limits that can be passed into sympy.integrate """ - return [(vars[1], sympy.Integer(0), sympy.Integer(1)), - (vars[0], sympy.Integer(0), sympy.Integer(1))] + return [ + (vars[1], sympy.Integer(0), sympy.Integer(1)), + (vars[0], sympy.Integer(0), sympy.Integer(1)), + ] def get_map_to(self, vertices: SetOfPointsInput) -> PointType: """Get the map from the reference to a cell. @@ -1443,7 +1515,8 @@ def get_map_to(self, vertices: SetOfPointsInput) -> PointType: assert self.vertices == self.reference_vertices return tuple( (1 - x[1]) * ((1 - x[0]) * v0 + x[0] * v1) + x[1] * ((1 - x[0]) * v2 + x[0] * v3) - for v0, v1, v2, v3 in zip(*vertices)) + for v0, v1, v2, v3 in zip(*vertices) + ) def get_inverse_map_to(self, vertices_in: SetOfPointsInput) -> PointType: """Get the inverse map from a cell to the reference. @@ -1462,13 +1535,12 @@ def get_inverse_map_to(self, vertices_in: SetOfPointsInput) -> PointType: v2 = _vsub(vertices[2], vertices[0]) if len(self.vertices[0]) == 2: - mat = sympy.Matrix([[v1[0], v2[0]], - [v1[1], v2[1]]]).inv() + mat = sympy.Matrix([[v1[0], v2[0]], [v1[1], v2[1]]]).inv() elif len(self.vertices[0]) == 3: v3 = _vcross(v1, v2) - mat = sympy.Matrix([[v1[0], v2[0], v3[0]], - [v1[1], v2[1], v3[1]], - [v1[2], v2[2], v3[2]]]).inv() + mat = sympy.Matrix( + [[v1[0], v2[0], v3[0]], [v1[1], v2[1], v3[1]], [v1[2], v2[2], v3[2]]] + ).inv() else: raise RuntimeError("Cannot get inverse map.") @@ -1482,7 +1554,8 @@ def _compute_map_to_self(self) -> PointType: """ return tuple( (1 - x[1]) * ((1 - x[0]) * v0 + x[0] * v1) + x[1] * ((1 - x[0]) * v2 + x[0] * v3) - for v0, v1, v2, v3 in zip(*self.vertices)) + for v0, v1, v2, v3 in zip(*self.vertices) + ) def _compute_inverse_map_to_self(self) -> PointType: """Compute the inverse map from the canonical reference to this reference. @@ -1490,20 +1563,20 @@ def _compute_inverse_map_to_self(self) -> PointType: Returns: The map """ - assert _vadd( - self.vertices[0], self.vertices[3]) == _vadd(self.vertices[1], self.vertices[2]) + assert _vadd(self.vertices[0], self.vertices[3]) == _vadd( + self.vertices[1], self.vertices[2] + ) p = _vsub(tuple(x), self.vertices[0]) v1 = _vsub(self.vertices[1], self.vertices[0]) v2 = _vsub(self.vertices[2], self.vertices[0]) if len(self.vertices[0]) == 2: - mat = sympy.Matrix([[v1[0], v2[0]], - [v1[1], v2[1]]]).inv() + mat = sympy.Matrix([[v1[0], v2[0]], [v1[1], v2[1]]]).inv() elif len(self.vertices[0]) == 3: v3 = _vcross(v1, v2) - mat = sympy.Matrix([[v1[0], v2[0], v3[0]], - [v1[1], v2[1], v3[1]], - [v1[2], v2[2], v3[2]]]).inv() + mat = sympy.Matrix( + [[v1[0], v2[0], v3[0]], [v1[1], v2[1], v3[1]], [v1[2], v2[2], v3[2]]] + ).inv() else: raise RuntimeError("Cannot get inverse map.") @@ -1534,8 +1607,18 @@ def contains(self, point: PointType) -> bool: class Hexahedron(Reference): """A hexahedron.""" - def __init__(self, vertices: SetOfPointsInput = ( - (0, 0, 0), (1, 0, 0), (0, 1, 0), (1, 1, 0), (0, 0, 1), (1, 0, 1), (0, 1, 1), (1, 1, 1)) + def __init__( + self, + vertices: SetOfPointsInput = ( + (0, 0, 0), + (1, 0, 0), + (0, 1, 0), + (1, 1, 0), + (0, 0, 1), + (1, 0, 1), + (0, 1, 1), + (1, 1, 1), + ), ): """Create a hexahedron. @@ -1553,18 +1636,42 @@ def __init__(self, vertices: SetOfPointsInput = ( _vsub(vertices[4], vertices[0]), ), reference_vertices=( - (0, 0, 0), (1, 0, 0), (0, 1, 0), (1, 1, 0), - (0, 0, 1), (1, 0, 1), (0, 1, 1), (1, 1, 1)), + (0, 0, 0), + (1, 0, 0), + (0, 1, 0), + (1, 1, 0), + (0, 0, 1), + (1, 0, 1), + (0, 1, 1), + (1, 1, 1), + ), vertices=vertices, edges=( - (0, 1), (0, 2), (0, 4), (1, 3), (1, 5), (2, 3), - (2, 6), (3, 7), (4, 5), (4, 6), (5, 7), (6, 7)), + (0, 1), + (0, 2), + (0, 4), + (1, 3), + (1, 5), + (2, 3), + (2, 6), + (3, 7), + (4, 5), + (4, 6), + (5, 7), + (6, 7), + ), faces=( - (0, 1, 2, 3), (0, 1, 4, 5), (0, 2, 4, 6), - (1, 3, 5, 7), (2, 3, 6, 7), (4, 5, 6, 7)), + (0, 1, 2, 3), + (0, 1, 4, 5), + (0, 2, 4, 6), + (1, 3, 5, 7), + (2, 3, 6, 7), + (4, 5, 6, 7), + ), volumes=((0, 1, 2, 3, 4, 5, 6, 7),), sub_entity_types=["point", "interval", "quadrilateral", "hexahedron"], - tp=True) + tp=True, + ) @property def clockwise_vertices(self) -> SetOfPoints: @@ -1573,8 +1680,14 @@ def clockwise_vertices(self) -> SetOfPoints: Returns: A list of vertices """ - return (self.vertices[0], self.vertices[1], self.vertices[3], self.vertices[7], - self.vertices[6], self.vertices[4]) + return ( + self.vertices[0], + self.vertices[1], + self.vertices[3], + self.vertices[7], + self.vertices[6], + self.vertices[4], + ) def z_ordered_entities(self) -> typing.List[typing.List[typing.Tuple[int, int]]]: """Get the subentities of the cell in back-to-front plotting order. @@ -1587,7 +1700,7 @@ def z_ordered_entities(self) -> typing.List[typing.List[typing.Tuple[int, int]]] [(3, 0)], [(2, 3), (1, 3), (1, 7), (0, 3)], [(2, 1), (1, 0), (1, 2), (1, 4), (0, 0), (0, 1)], - [(2, 5), (1, 8), (1, 9), (1, 10), (1, 11), (0, 4), (0, 5), (0, 6), (0, 7)] + [(2, 5), (1, 8), (1, 9), (1, 10), (1, 11), (0, 4), (0, 5), (0, 6), (0, 7)], ] def default_reference(self) -> Reference: @@ -1608,11 +1721,16 @@ def make_lattice(self, n: int) -> SetOfPoints: A lattice of points offset from the edge of the cell """ assert self.vertices == self.reference_vertices - return tuple(( - sympy.Rational(2 * i + 1, 2 * (n + 1)), - sympy.Rational(2 * j + 1, 2 * (n + 1)), - sympy.Rational(2 * k + 1, 2 * (n + 1)) - ) for i in range(n + 1) for j in range(n + 1) for k in range(n + 1)) + return tuple( + ( + sympy.Rational(2 * i + 1, 2 * (n + 1)), + sympy.Rational(2 * j + 1, 2 * (n + 1)), + sympy.Rational(2 * k + 1, 2 * (n + 1)), + ) + for i in range(n + 1) + for j in range(n + 1) + for k in range(n + 1) + ) def make_lattice_with_lines(self, n: int) -> LatticeWithLines: """Make a lattice of points, and a list of lines connecting them. @@ -1635,9 +1753,11 @@ def integration_limits(self, vars: AxisVariablesNotSingle = t) -> IntLimits: Returns: Integration limits that can be passed into sympy.integrate """ - return [(vars[2], sympy.Integer(0), sympy.Integer(1)), - (vars[1], sympy.Integer(0), sympy.Integer(1)), - (vars[0], sympy.Integer(0), sympy.Integer(1))] + return [ + (vars[2], sympy.Integer(0), sympy.Integer(1)), + (vars[1], sympy.Integer(0), sympy.Integer(1)), + (vars[0], sympy.Integer(0), sympy.Integer(1)), + ] def get_map_to(self, vertices: SetOfPointsInput) -> PointType: """Get the map from the reference to a cell. @@ -1650,11 +1770,12 @@ def get_map_to(self, vertices: SetOfPointsInput) -> PointType: """ assert self.vertices == self.reference_vertices return tuple( - (1 - x[2]) * ((1 - x[1]) * ((1 - x[0]) * v0 + x[0] * v1) - + x[1] * ((1 - x[0]) * v2 + x[0] * v3)) - + x[2] * ((1 - x[1]) * ((1 - x[0]) * v4 + x[0] * v5) - + x[1] * ((1 - x[0]) * v6 + x[0] * v7)) - for v0, v1, v2, v3, v4, v5, v6, v7 in zip(*vertices)) + (1 - x[2]) + * ((1 - x[1]) * ((1 - x[0]) * v0 + x[0] * v1) + x[1] * ((1 - x[0]) * v2 + x[0] * v3)) + + x[2] + * ((1 - x[1]) * ((1 - x[0]) * v4 + x[0] * v5) + x[1] * ((1 - x[0]) * v6 + x[0] * v7)) + for v0, v1, v2, v3, v4, v5, v6, v7 in zip(*vertices) + ) def get_inverse_map_to(self, vertices_in: SetOfPointsInput) -> PointType: """Get the inverse map from a cell to the reference. @@ -1674,9 +1795,9 @@ def get_inverse_map_to(self, vertices_in: SetOfPointsInput) -> PointType: v1 = _vsub(vertices[1], vertices[0]) v2 = _vsub(vertices[2], vertices[0]) v3 = _vsub(vertices[4], vertices[0]) - mat = sympy.Matrix([[v1[0], v2[0], v3[0]], - [v1[1], v2[1], v3[1]], - [v1[2], v2[2], v3[2]]]).inv() + mat = sympy.Matrix( + [[v1[0], v2[0], v3[0]], [v1[1], v2[1], v3[1]], [v1[2], v2[2], v3[2]]] + ).inv() return tuple(_vdot(mat.row(i), p) for i in range(mat.rows)) def _compute_map_to_self(self) -> PointType: @@ -1686,11 +1807,12 @@ def _compute_map_to_self(self) -> PointType: The map """ return tuple( - (1 - x[2]) * ((1 - x[1]) * ((1 - x[0]) * v0 + x[0] * v1) - + x[1] * ((1 - x[0]) * v2 + x[0] * v3)) - + x[2] * ((1 - x[1]) * ((1 - x[0]) * v4 + x[0] * v5) - + x[1] * ((1 - x[0]) * v6 + x[0] * v7)) - for v0, v1, v2, v3, v4, v5, v6, v7 in zip(*self.vertices)) + (1 - x[2]) + * ((1 - x[1]) * ((1 - x[0]) * v0 + x[0] * v1) + x[1] * ((1 - x[0]) * v2 + x[0] * v3)) + + x[2] + * ((1 - x[1]) * ((1 - x[0]) * v4 + x[0] * v5) + x[1] * ((1 - x[0]) * v6 + x[0] * v7)) + for v0, v1, v2, v3, v4, v5, v6, v7 in zip(*self.vertices) + ) def _compute_inverse_map_to_self(self) -> PointType: """Compute the inverse map from the canonical reference to this reference. @@ -1700,15 +1822,16 @@ def _compute_inverse_map_to_self(self) -> PointType: """ assert len(self.vertices[0]) == 3 for a, b, c, d in self.faces: - assert _vadd( - self.vertices[a], self.vertices[d]) == _vadd(self.vertices[b], self.vertices[c]) + assert _vadd(self.vertices[a], self.vertices[d]) == _vadd( + self.vertices[b], self.vertices[c] + ) p = _vsub(tuple(x), self.vertices[0]) v1 = _vsub(self.vertices[1], self.vertices[0]) v2 = _vsub(self.vertices[2], self.vertices[0]) v3 = _vsub(self.vertices[4], self.vertices[0]) - mat = sympy.Matrix([[v1[0], v2[0], v3[0]], - [v1[1], v2[1], v3[1]], - [v1[2], v2[2], v3[2]]]).inv() + mat = sympy.Matrix( + [[v1[0], v2[0], v3[0]], [v1[1], v2[1], v3[1]], [v1[2], v2[2], v3[2]]] + ).inv() return tuple(_vdot(mat.row(i), p) for i in range(mat.rows)) def volume(self) -> sympy.core.expr.Expr: @@ -1736,8 +1859,16 @@ def contains(self, point: PointType) -> bool: class Prism(Reference): """A (triangular) prism.""" - def __init__(self, vertices: SetOfPointsInput = ( - (0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1), (1, 0, 1), (0, 1, 1)) + def __init__( + self, + vertices: SetOfPointsInput = ( + (0, 0, 0), + (1, 0, 0), + (0, 1, 0), + (0, 0, 1), + (1, 0, 1), + (0, 1, 1), + ), ): """Create a prism. @@ -1754,22 +1885,19 @@ def __init__(self, vertices: SetOfPointsInput = ( _vsub(vertices[2], vertices[0]), _vsub(vertices[3], vertices[0]), ), - reference_vertices=( - (0, 0, 0), (1, 0, 0), (0, 1, 0), - (0, 0, 1), (1, 0, 1), (0, 1, 1)), + reference_vertices=((0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1), (1, 0, 1), (0, 1, 1)), vertices=vertices, - edges=( - (0, 1), (0, 2), (0, 3), (1, 2), (1, 4), - (2, 5), (3, 4), (3, 5), (4, 5)), - faces=( - (0, 1, 2), (0, 1, 3, 4), (0, 2, 3, 5), - (1, 2, 4, 5), (3, 4, 5)), + edges=((0, 1), (0, 2), (0, 3), (1, 2), (1, 4), (2, 5), (3, 4), (3, 5), (4, 5)), + faces=((0, 1, 2), (0, 1, 3, 4), (0, 2, 3, 5), (1, 2, 4, 5), (3, 4, 5)), volumes=((0, 1, 2, 3, 4, 5),), sub_entity_types=[ - "point", "interval", + "point", + "interval", ["triangle", "quadrilateral", "quadrilateral", "quadrilateral", "triangle"], - "prism"], - tp=True) + "prism", + ], + tp=True, + ) @property def clockwise_vertices(self) -> SetOfPoints: @@ -1778,8 +1906,13 @@ def clockwise_vertices(self) -> SetOfPoints: Returns: A list of vertices """ - return (self.vertices[0], self.vertices[1], self.vertices[4], self.vertices[5], - self.vertices[3]) + return ( + self.vertices[0], + self.vertices[1], + self.vertices[4], + self.vertices[5], + self.vertices[3], + ) def z_ordered_entities(self) -> typing.List[typing.List[typing.Tuple[int, int]]]: """Get the subentities of the cell in back-to-front plotting order. @@ -1792,7 +1925,7 @@ def z_ordered_entities(self) -> typing.List[typing.List[typing.Tuple[int, int]]] [(2, 2), (1, 1), (1, 5), (0, 2)], [(3, 0)], [(2, 1), (1, 0), (1, 2), (1, 4), (0, 0), (0, 1)], - [(2, 4), (1, 6), (1, 7), (1, 8), (0, 3), (0, 4), (0, 5)] + [(2, 4), (1, 6), (1, 7), (1, 8), (0, 3), (0, 4), (0, 5)], ] def default_reference(self) -> Reference: @@ -1813,11 +1946,16 @@ def make_lattice(self, n: int) -> SetOfPoints: A lattice of points offset from the edge of the cell """ assert self.vertices == self.reference_vertices - return tuple(( - sympy.Rational(2 * i + 1, 2 * (n + 1)), - sympy.Rational(2 * j + 1, 2 * (n + 1)), - sympy.Rational(2 * k + 1, 2 * (n + 1)) - ) for i in range(n + 1) for j in range(n + 1 - i) for k in range(n + 1)) + return tuple( + ( + sympy.Rational(2 * i + 1, 2 * (n + 1)), + sympy.Rational(2 * j + 1, 2 * (n + 1)), + sympy.Rational(2 * k + 1, 2 * (n + 1)), + ) + for i in range(n + 1) + for j in range(n + 1 - i) + for k in range(n + 1) + ) def make_lattice_with_lines(self, n: int) -> LatticeWithLines: """Make a lattice of points, and a list of lines connecting them. @@ -1840,9 +1978,11 @@ def integration_limits(self, vars: AxisVariablesNotSingle = t) -> IntLimits: Returns: Integration limits that can be passed into sympy.integrate """ - return [(vars[2], sympy.Integer(0), sympy.Integer(1)), - (vars[1], sympy.Integer(0), sympy.Integer(1) - vars[0]), - (vars[0], sympy.Integer(0), sympy.Integer(1))] + return [ + (vars[2], sympy.Integer(0), sympy.Integer(1)), + (vars[1], sympy.Integer(0), sympy.Integer(1) - vars[0]), + (vars[0], sympy.Integer(0), sympy.Integer(1)), + ] def get_map_to(self, vertices: SetOfPointsInput) -> PointType: """Get the map from the reference to a cell. @@ -1857,7 +1997,8 @@ def get_map_to(self, vertices: SetOfPointsInput) -> PointType: return tuple( (1 - x[2]) * (v0 + x[0] * (v1 - v0) + x[1] * (v2 - v0)) + x[2] * (v3 + x[0] * (v4 - v3) + x[1] * (v5 - v3)) - for v0, v1, v2, v3, v4, v5 in zip(*vertices)) + for v0, v1, v2, v3, v4, v5 in zip(*vertices) + ) def get_inverse_map_to(self, vertices_in: SetOfPointsInput) -> PointType: """Get the inverse map from a cell to the reference. @@ -1877,9 +2018,9 @@ def get_inverse_map_to(self, vertices_in: SetOfPointsInput) -> PointType: v1 = _vsub(vertices[1], vertices[0]) v2 = _vsub(vertices[2], vertices[0]) v3 = _vsub(vertices[3], vertices[0]) - mat = sympy.Matrix([[v1[0], v2[0], v3[0]], - [v1[1], v2[1], v3[1]], - [v1[2], v2[2], v3[2]]]).inv() + mat = sympy.Matrix( + [[v1[0], v2[0], v3[0]], [v1[1], v2[1], v3[1]], [v1[2], v2[2], v3[2]]] + ).inv() return tuple(_vdot(mat.row(i), p) for i in range(mat.rows)) def _compute_map_to_self(self) -> PointType: @@ -1891,7 +2032,8 @@ def _compute_map_to_self(self) -> PointType: return tuple( (1 - x[2]) * (v0 + x[0] * (v1 - v0) + x[1] * (v2 - v0)) + x[2] * (v3 + x[0] * (v4 - v3) + x[1] * (v5 - v3)) - for v0, v1, v2, v3, v4, v5 in zip(*self.vertices)) + for v0, v1, v2, v3, v4, v5 in zip(*self.vertices) + ) def _compute_inverse_map_to_self(self) -> PointType: """Compute the inverse map from the canonical reference to this reference. @@ -1901,15 +2043,16 @@ def _compute_inverse_map_to_self(self) -> PointType: """ assert len(self.vertices[0]) == 3 for a, b, c, d in self.faces[1:4]: - assert _vadd( - self.vertices[a], self.vertices[d]) == _vadd(self.vertices[b], self.vertices[c]) + assert _vadd(self.vertices[a], self.vertices[d]) == _vadd( + self.vertices[b], self.vertices[c] + ) p = _vsub(tuple(x), self.vertices[0]) v1 = _vsub(self.vertices[1], self.vertices[0]) v2 = _vsub(self.vertices[2], self.vertices[0]) v3 = _vsub(self.vertices[3], self.vertices[0]) - mat = sympy.Matrix([[v1[0], v2[0], v3[0]], - [v1[1], v2[1], v3[1]], - [v1[2], v2[2], v3[2]]]).inv() + mat = sympy.Matrix( + [[v1[0], v2[0], v3[0]], [v1[1], v2[1], v3[1]], [v1[2], v2[2], v3[2]]] + ).inv() return tuple(_vdot(mat.row(i), p) for i in range(mat.rows)) def volume(self) -> sympy.core.expr.Expr: @@ -1931,15 +2074,20 @@ def contains(self, point: PointType) -> bool: """ if self.vertices != self.reference_vertices: raise NotImplementedError() - return (point[0] >= 0 and point[1] >= 0 and point[2] >= 0 - and point[2] <= 1 and point[0] + point[1] <= 1) + return ( + point[0] >= 0 + and point[1] >= 0 + and point[2] >= 0 + and point[2] <= 1 + and point[0] + point[1] <= 1 + ) class Pyramid(Reference): """A (square-based) pyramid.""" - def __init__(self, vertices: SetOfPointsInput = ( - (0, 0, 0), (1, 0, 0), (0, 1, 0), (1, 1, 0), (0, 0, 1)) + def __init__( + self, vertices: SetOfPointsInput = ((0, 0, 0), (1, 0, 0), (0, 1, 0), (1, 1, 0), (0, 0, 1)) ): """Create a pyramid. @@ -1956,22 +2104,19 @@ def __init__(self, vertices: SetOfPointsInput = ( _vsub(vertices[2], vertices[0]), _vsub(vertices[4], vertices[0]), ), - reference_vertices=( - (0, 0, 0), (1, 0, 0), (0, 1, 0), - (1, 1, 0), (0, 0, 1)), + reference_vertices=((0, 0, 0), (1, 0, 0), (0, 1, 0), (1, 1, 0), (0, 0, 1)), vertices=vertices, - edges=( - (0, 1), (0, 2), (0, 4), (1, 3), - (1, 4), (2, 3), (2, 4), (3, 4)), - faces=( - (0, 1, 2, 3), (0, 1, 4), (0, 2, 4), - (1, 3, 4), (2, 3, 4)), + edges=((0, 1), (0, 2), (0, 4), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)), + faces=((0, 1, 2, 3), (0, 1, 4), (0, 2, 4), (1, 3, 4), (2, 3, 4)), volumes=((0, 1, 2, 3, 4),), sub_entity_types=[ - "point", "interval", + "point", + "interval", ["quadrilateral", "triangle", "triangle", "triangle", "triangle"], - "pyramid"], - tp=True) + "pyramid", + ], + tp=True, + ) @property def clockwise_vertices(self) -> SetOfPoints: @@ -1993,7 +2138,7 @@ def z_ordered_entities(self) -> typing.List[typing.List[typing.Tuple[int, int]]] [(2, 2), (1, 1), (1, 6), (0, 2)], [(3, 0)], [(2, 3), (1, 3), (1, 7), (0, 3)], - [(2, 1), (1, 0), (1, 2), (1, 4), (0, 0), (0, 1), (0, 4)] + [(2, 1), (1, 0), (1, 2), (1, 4), (0, 0), (0, 1), (0, 4)], ] def default_reference(self) -> Reference: @@ -2036,9 +2181,11 @@ def integration_limits(self, vars: AxisVariablesNotSingle = t) -> IntLimits: Returns: Integration limits that can be passed into sympy.integrate """ - return [(vars[0], sympy.Integer(0), 1 - vars[2]), - (vars[1], sympy.Integer(0), 1 - vars[2]), - (vars[2], sympy.Integer(0), sympy.Integer(1))] + return [ + (vars[0], sympy.Integer(0), 1 - vars[2]), + (vars[1], sympy.Integer(0), 1 - vars[2]), + (vars[2], sympy.Integer(0), sympy.Integer(1)), + ] def get_map_to(self, vertices: SetOfPointsInput) -> PointType: """Get the map from the reference to a cell. @@ -2051,11 +2198,11 @@ def get_map_to(self, vertices: SetOfPointsInput) -> PointType: """ assert self.vertices == self.reference_vertices return tuple( - (1 - x[2]) * ( - (1 - x[1]) * ((1 - x[0]) * v0 + x[0] * v1) - + x[1] * ((1 - x[0]) * v2 + x[0] * v3) - ) + x[2] * v4 - for v0, v1, v2, v3, v4 in zip(*vertices)) + (1 - x[2]) + * ((1 - x[1]) * ((1 - x[0]) * v0 + x[0] * v1) + x[1] * ((1 - x[0]) * v2 + x[0] * v3)) + + x[2] * v4 + for v0, v1, v2, v3, v4 in zip(*vertices) + ) def get_inverse_map_to(self, vertices_in: SetOfPointsInput) -> PointType: """Get the inverse map from a cell to the reference. @@ -2075,9 +2222,9 @@ def get_inverse_map_to(self, vertices_in: SetOfPointsInput) -> PointType: v1 = _vsub(vertices[1], vertices[0]) v2 = _vsub(vertices[2], vertices[0]) v3 = _vsub(vertices[4], vertices[0]) - mat = sympy.Matrix([[v1[0], v2[0], v3[0]], - [v1[1], v2[1], v3[1]], - [v1[2], v2[2], v3[2]]]).inv() + mat = sympy.Matrix( + [[v1[0], v2[0], v3[0]], [v1[1], v2[1], v3[1]], [v1[2], v2[2], v3[2]]] + ).inv() return tuple(_vdot(mat.row(i), p) for i in range(mat.rows)) def _compute_map_to_self(self) -> PointType: @@ -2087,11 +2234,11 @@ def _compute_map_to_self(self) -> PointType: The map """ return tuple( - (1 - x[2]) * ( - (1 - x[1]) * ((1 - x[0]) * v0 + x[0] * v1) - + x[1] * ((1 - x[0]) * v2 + x[0] * v3) - ) + x[2] * v4 - for v0, v1, v2, v3, v4 in zip(*self.vertices)) + (1 - x[2]) + * ((1 - x[1]) * ((1 - x[0]) * v0 + x[0] * v1) + x[1] * ((1 - x[0]) * v2 + x[0] * v3)) + + x[2] * v4 + for v0, v1, v2, v3, v4 in zip(*self.vertices) + ) def _compute_inverse_map_to_self(self) -> PointType: """Compute the inverse map from the canonical reference to this reference. @@ -2101,15 +2248,16 @@ def _compute_inverse_map_to_self(self) -> PointType: """ assert len(self.vertices[0]) == 3 for a, b, c, d in self.faces[:1]: - assert _vadd( - self.vertices[a], self.vertices[d]) == _vadd(self.vertices[b], self.vertices[c]) + assert _vadd(self.vertices[a], self.vertices[d]) == _vadd( + self.vertices[b], self.vertices[c] + ) p = _vsub(tuple(x), self.vertices[0]) v1 = _vsub(self.vertices[1], self.vertices[0]) v2 = _vsub(self.vertices[2], self.vertices[0]) v3 = _vsub(self.vertices[4], self.vertices[0]) - mat = sympy.Matrix([[v1[0], v2[0], v3[0]], - [v1[1], v2[1], v3[1]], - [v1[2], v2[2], v3[2]]]).inv() + mat = sympy.Matrix( + [[v1[0], v2[0], v3[0]], [v1[1], v2[1], v3[1]], [v1[2], v2[2], v3[2]]] + ).inv() return tuple(_vdot(mat.row(i), p) for i in range(mat.rows)) def volume(self) -> sympy.core.expr.Expr: @@ -2131,8 +2279,13 @@ def contains(self, point: PointType) -> bool: """ if self.vertices != self.reference_vertices: raise NotImplementedError() - return (point[0] >= 0 and point[1] >= 0 and point[2] >= 0 - and point[0] + point[2] <= 1 and point[1] + point[2] <= 1) + return ( + point[0] >= 0 + and point[1] >= 0 + and point[2] >= 0 + and point[0] + point[2] <= 1 + and point[1] + point[2] <= 1 + ) class DualPolygon(Reference): @@ -2158,8 +2311,11 @@ def __init__( reference_vertices.append((sympy.cos(angle), sympy.sin(angle))) reference_vertices.append( - ((sympy.cos(next_angle) + sympy.cos(angle)) / 2, - (sympy.sin(next_angle) + sympy.sin(angle)) / 2)) + ( + (sympy.cos(next_angle) + sympy.cos(angle)) / 2, + (sympy.sin(next_angle) + sympy.sin(angle)) / 2, + ) + ) origin: PointType = self.reference_origin if vertices is None: @@ -2176,12 +2332,12 @@ def __init__( origin=origin, vertices=vertices, reference_vertices=tuple(reference_vertices), - edges=tuple((i, (i + 1) % (2 * number_of_triangles)) - for i in range(2 * number_of_triangles)), - faces=(tuple(range(2 * number_of_triangles)), ), + edges=tuple( + (i, (i + 1) % (2 * number_of_triangles)) for i in range(2 * number_of_triangles) + ), + faces=(tuple(range(2 * number_of_triangles)),), volumes=(), sub_entity_types=["point", "interval", f"dual polygon({number_of_triangles})", None], - ) def contains(self, point: PointType) -> bool: @@ -2270,9 +2426,10 @@ def make_lattice(self, n: int) -> SetOfPoints: A lattice of points offset from the edge of the cell """ assert self.vertices == self.reference_vertices - from .create import create_reference + from symfem.create import create_reference + lattice: SetOfPoints = () - for v1, v2 in zip(self.vertices, self.vertices[1:] + (self.vertices[0], )): + for v1, v2 in zip(self.vertices, self.vertices[1:] + (self.vertices[0],)): ref = create_reference("triangle", (self.origin, v1, v2)) lattice += ref.make_lattice(n // 2) return lattice @@ -2299,5 +2456,5 @@ def z_ordered_entities_extra_dim(self) -> typing.List[typing.List[typing.Tuple[i n = (self.number_of_triangles + 1) // 2 * 2 return [ [(1, i) for i in range(n)] + [(0, i) for i in range(1, n)], - [(2, 0)] + [(1, i) for i in range(n, N)] + [(0, 0)] + [(0, i) for i in range(n, N)] + [(2, 0)] + [(1, i) for i in range(n, N)] + [(0, 0)] + [(0, i) for i in range(n, N)], ] diff --git a/symfem/symbols.py b/symfem/symbols.py index 5fe3f865..f2a54756 100644 --- a/symfem/symbols.py +++ b/symfem/symbols.py @@ -8,6 +8,6 @@ t = (sympy.Symbol("t0"), sympy.Symbol("t1"), sympy.Symbol("t2")) AxisVariablesNotSingle = typing.Union[ - typing.Tuple[sympy.core.symbol.Symbol, ...], - typing.List[sympy.core.symbol.Symbol]] + typing.Tuple[sympy.core.symbol.Symbol, ...], typing.List[sympy.core.symbol.Symbol] +] AxisVariables = typing.Union[AxisVariablesNotSingle, sympy.core.symbol.Symbol] diff --git a/symfem/utils.py b/symfem/utils.py index 77b323d2..58df9927 100644 --- a/symfem/utils.py +++ b/symfem/utils.py @@ -4,7 +4,7 @@ import sympy -from .functions import ScalarFunction +from symfem.functions import ScalarFunction def allequal(a: typing.Any, b: typing.Any) -> bool: diff --git a/test/test_against_basix.py b/test/test_against_basix.py index a5dbe563..3ea2db35 100644 --- a/test/test_against_basix.py +++ b/test/test_against_basix.py @@ -6,15 +6,23 @@ from symfem import create_element -elements: typing.Dict[str, typing.List[typing.Tuple[ - str, str, typing.Iterable, typing.List[typing.Tuple[str, typing.Any]] -]]] = { +elements: typing.Dict[ + str, + typing.List[ + typing.Tuple[str, str, typing.Iterable, typing.List[typing.Tuple[str, typing.Any]]] + ], +] = { "interval": [ ("P", "Lagrange", range(1, 4), [("LagrangeVariant", "equispaced")]), - ("serendipity", "Serendipity", range(1, 5), [("LagrangeVariant", "equispaced"), - ("DPCVariant", "simplex_equispaced")]), + ( + "serendipity", + "Serendipity", + range(1, 5), + [("LagrangeVariant", "equispaced"), ("DPCVariant", "simplex_equispaced")], + ), ("bubble", "Bubble", range(2, 5), []), - ("dPc", "DPC", range(0, 5), [("LagrangeVariant", "equispaced"), ("bool", True)])], + ("dPc", "DPC", range(0, 5), [("LagrangeVariant", "equispaced"), ("bool", True)]), + ], "triangle": [ ("P", "Lagrange", range(1, 4), [("LagrangeVariant", "equispaced")]), ("bubble", "Bubble", range(3, 5), []), @@ -24,7 +32,8 @@ ("N2div", "Brezzi-Douglas-Marini", range(1, 4), [("LagrangeVariant", "equispaced")]), # ("Regge", "Regge", range(0, 4), []), # ("HHJ", "Hellan-Herrmann-Johnson", range(0, 4), []), - ("Crouzeix-Raviart", "Crouzeix-Raviart", [1], [])], + ("Crouzeix-Raviart", "Crouzeix-Raviart", [1], []), + ], "tetrahedron": [ ("P", "Lagrange", range(1, 4), [("LagrangeVariant", "equispaced")]), ("bubble", "Bubble", range(4, 6), []), @@ -33,32 +42,57 @@ ("N1div", "Raviart-Thomas", range(1, 3), [("LagrangeVariant", "equispaced")]), ("N2div", "Brezzi-Douglas-Marini", range(1, 3), [("LagrangeVariant", "equispaced")]), # ("Regge", "Regge", range(0, 3), []), - ("Crouzeix-Raviart", "Crouzeix-Raviart", [1], [])], + ("Crouzeix-Raviart", "Crouzeix-Raviart", [1], []), + ], "quadrilateral": [ ("Q", "Lagrange", range(1, 4), [("LagrangeVariant", "equispaced")]), ("dPc", "DPC", range(0, 4), [("DPCVariant", "simplex_equispaced"), ("bool", True)]), - ("serendipity", "Serendipity", range(1, 5), - [("LagrangeVariant", "equispaced"), ("DPCVariant", "simplex_equispaced")]), + ( + "serendipity", + "Serendipity", + range(1, 5), + [("LagrangeVariant", "equispaced"), ("DPCVariant", "simplex_equispaced")], + ), ("Qdiv", "Raviart-Thomas", range(1, 4), [("LagrangeVariant", "equispaced")]), ("Qcurl", "Nedelec 1st kind H(curl)", range(1, 4), [("LagrangeVariant", "equispaced")]), - ("Sdiv", "Brezzi-Douglas-Marini", range(1, 4), - [("LagrangeVariant", "equispaced"), ("DPCVariant", "simplex_equispaced")]), - ("Scurl", "Nedelec 2nd kind H(curl)", range(1, 4), - [("LagrangeVariant", "equispaced"), ("DPCVariant", "simplex_equispaced")])], + ( + "Sdiv", + "Brezzi-Douglas-Marini", + range(1, 4), + [("LagrangeVariant", "equispaced"), ("DPCVariant", "simplex_equispaced")], + ), + ( + "Scurl", + "Nedelec 2nd kind H(curl)", + range(1, 4), + [("LagrangeVariant", "equispaced"), ("DPCVariant", "simplex_equispaced")], + ), + ], "hexahedron": [ ("Q", "Lagrange", range(1, 3), [("LagrangeVariant", "equispaced")]), - ("dPc", "DPC", range(0, 3), - [("DPCVariant", "simplex_equispaced"), ("bool", True)]), - ("serendipity", "Serendipity", range(1, 5), - [("LagrangeVariant", "equispaced"), ("DPCVariant", "simplex_equispaced")]), + ("dPc", "DPC", range(0, 3), [("DPCVariant", "simplex_equispaced"), ("bool", True)]), + ( + "serendipity", + "Serendipity", + range(1, 5), + [("LagrangeVariant", "equispaced"), ("DPCVariant", "simplex_equispaced")], + ), ("Qdiv", "Raviart-Thomas", range(1, 3), [("LagrangeVariant", "equispaced")]), ("Qcurl", "Nedelec 1st kind H(curl)", range(1, 3), [("LagrangeVariant", "equispaced")]), - ("Sdiv", "Brezzi-Douglas-Marini", range(1, 3), - [("LagrangeVariant", "equispaced"), ("DPCVariant", "simplex_equispaced")]), - ("Scurl", "Nedelec 2nd kind H(curl)", range(1, 3), - [("LagrangeVariant", "equispaced"), ("DPCVariant", "simplex_equispaced")])], - "prism": [ - ("Lagrange", "Lagrange", range(1, 4), [("LagrangeVariant", "equispaced")])] + ( + "Sdiv", + "Brezzi-Douglas-Marini", + range(1, 3), + [("LagrangeVariant", "equispaced"), ("DPCVariant", "simplex_equispaced")], + ), + ( + "Scurl", + "Nedelec 2nd kind H(curl)", + range(1, 3), + [("LagrangeVariant", "equispaced"), ("DPCVariant", "simplex_equispaced")], + ), + ], + "prism": [("Lagrange", "Lagrange", range(1, 4), [("LagrangeVariant", "equispaced")])], } @@ -71,6 +105,7 @@ def to_float(a): def to_nparray(a): import numpy as np + try: return float(a) except: # noqa: E722 @@ -79,33 +114,63 @@ def to_nparray(a): def make_lattice(cell, N=3): import numpy as np + if cell == "interval": return np.array([[i / N] for i in range(N + 1)]) if cell == "triangle": return np.array([[i / N, j / N] for i in range(N + 1) for j in range(N + 1 - i)]) if cell == "tetrahedron": - return np.array([[i / N, j / N, k / N] - for i in range(N + 1) for j in range(N + 1 - i) - for k in range(N + 1 - i - j)]) + return np.array( + [ + [i / N, j / N, k / N] + for i in range(N + 1) + for j in range(N + 1 - i) + for k in range(N + 1 - i - j) + ] + ) if cell == "quadrilateral": return np.array([[i / N, j / N] for i in range(N + 1) for j in range(N + 1)]) if cell == "hexahedron": - return np.array([[i / N, j / N, k / N] - for i in range(N + 1) for j in range(N + 1) for k in range(N + 1)]) + return np.array( + [ + [i / N, j / N, k / N] + for i in range(N + 1) + for j in range(N + 1) + for k in range(N + 1) + ] + ) if cell == "prism": - return np.array([[i / N, j / N, k / N] - for i in range(N + 1) for j in range(N + 1 - i) for k in range(N + 1)]) + return np.array( + [ + [i / N, j / N, k / N] + for i in range(N + 1) + for j in range(N + 1 - i) + for k in range(N + 1) + ] + ) if cell == "pyramid": - return np.array([[i / N, j / N, k / N] - for i in range(N + 1) for j in range(N + 1) - for k in range(N + 1 - max(i, j))]) - - -@pytest.mark.parametrize(("cell", "symfem_type", "basix_type", "order", "args"), - [(cell, a, b, order, args) for cell, ls in elements.items() - for a, b, orders, args in ls for order in orders]) -def test_against_basix(has_basix, elements_to_test, cells_to_test, cell, symfem_type, - basix_type, order, args, speed): + return np.array( + [ + [i / N, j / N, k / N] + for i in range(N + 1) + for j in range(N + 1) + for k in range(N + 1 - max(i, j)) + ] + ) + + +@pytest.mark.parametrize( + ("cell", "symfem_type", "basix_type", "order", "args"), + [ + (cell, a, b, order, args) + for cell, ls in elements.items() + for a, b, orders, args in ls + for order in orders + ], +) +def test_against_basix( + has_basix, elements_to_test, cells_to_test, cell, symfem_type, basix_type, order, args, speed +): if elements_to_test != "ALL" and symfem_type not in elements_to_test: pytest.skip() if cells_to_test != "ALL" and cell not in cells_to_test: @@ -131,11 +196,7 @@ def test_against_basix(has_basix, elements_to_test, cells_to_test, cell, symfem_ import numpy as np points = make_lattice(cell, 2) - parsed_args = [ - basix.LagrangeVariant.unset, - basix.DPCVariant.unset, - False - ] + parsed_args = [basix.LagrangeVariant.unset, basix.DPCVariant.unset, False] for a in args: if a[0] == "LagrangeVariant": parsed_args[0] = basix.LagrangeVariant[a[1]] @@ -147,7 +208,10 @@ def test_against_basix(has_basix, elements_to_test, cells_to_test, cell, symfem_ raise ValueError(f"Unknown arg type: {a[0]}") space = basix.create_element( basix.finite_element.string_to_family(basix_type, cell), - basix.CellType[cell], order, *parsed_args) + basix.CellType[cell], + order, + *parsed_args, + ) result = space.tabulate(0, points)[0] element = create_element(cell, symfem_type, order) diff --git a/test/test_against_computed_by_hand.py b/test/test_against_computed_by_hand.py index 390b2629..d5435acf 100644 --- a/test/test_against_computed_by_hand.py +++ b/test/test_against_computed_by_hand.py @@ -44,8 +44,7 @@ def test_dual0(): space = create_element("dual polygon(4)", "dual", 0) q = sympy.Rational(1, 4) assert allequal( - space.tabulate_basis([[q, q], [-q, q], [-q, -q], [q, -q]]), - ((1, ), (1, ), (1, ), (1, )) + space.tabulate_basis([[q, q], [-q, q], [-q, -q], [q, -q]]), ((1,), (1,), (1,), (1,)) ) @@ -56,9 +55,11 @@ def test_dual1(): e = sympy.Rational(1, 8) assert allequal( space.tabulate_basis([[0, 0], [q, q], [h, 0]]), - ((q, q, q, q), - (sympy.Rational(5, 8), e, e, e), - (sympy.Rational(3, 8), e, e, sympy.Rational(3, 8))) + ( + (q, q, q, q), + (sympy.Rational(5, 8), e, e, e), + (sympy.Rational(3, 8), e, e, sympy.Rational(3, 8)), + ), ) @@ -67,12 +68,26 @@ def test_lagrange_pyramid(): x_i = x[0] / (1 - x[2]) y_i = x[1] / (1 - x[2]) z_i = x[2] / (1 - x[2]) - basis = [ScalarFunction(f) for f in [ - (1 - x_i) * (1 - y_i) / (1 + z_i), x_i * (1 - y_i) / (1 + z_i), - (1 - x_i) * y_i / (1 + z_i), x_i * y_i / (1 + z_i), z_i / (1 + z_i)]] + basis = [ + ScalarFunction(f) + for f in [ + (1 - x_i) * (1 - y_i) / (1 + z_i), + x_i * (1 - y_i) / (1 + z_i), + (1 - x_i) * y_i / (1 + z_i), + x_i * y_i / (1 + z_i), + z_i / (1 + z_i), + ] + ] assert allequal(basis, space.get_basis_functions()) - basis = [ScalarFunction(f) for f in [ - (1 - x[0] - x[2]) * (1 - x[1] - x[2]) / (1 - x[2]), x[0] * (1 - x[1] - x[2]) / (1 - x[2]), - (1 - x[0] - x[2]) * x[1] / (1 - x[2]), x[0] * x[1] / (1 - x[2]), x[2]]] + basis = [ + ScalarFunction(f) + for f in [ + (1 - x[0] - x[2]) * (1 - x[1] - x[2]) / (1 - x[2]), + x[0] * (1 - x[1] - x[2]) / (1 - x[2]), + (1 - x[0] - x[2]) * x[1] / (1 - x[2]), + x[0] * x[1] / (1 - x[2]), + x[2], + ] + ] assert allequal(basis, space.get_basis_functions()) diff --git a/test/test_alfeld_sorokina.py b/test/test_alfeld_sorokina.py index 4426f5d1..1cd9ead9 100644 --- a/test/test_alfeld_sorokina.py +++ b/test/test_alfeld_sorokina.py @@ -17,7 +17,7 @@ def test_continuity(): f2 = f.get_piece((half, half)) div_f1 = f1.div() div_f2 = f2.div() - line = ((1 - 2 * t[0], t[0])) + line = (1 - 2 * t[0], t[0]) f1 = f1.subs(x[:2], line) f2 = f2.subs(x[:2], line) div_f1 = div_f1.subs(x[:2], line) @@ -30,7 +30,7 @@ def test_continuity(): f2 = f.get_piece((0, half)) div_f1 = f1.div() div_f2 = f2.div() - line = ((t[0], 1 - 2 * t[0])) + line = (t[0], 1 - 2 * t[0]) f1 = f1.subs(x[:2], line) f2 = f2.subs(x[:2], line) div_f1 = div_f1.subs(x[:2], line) @@ -43,7 +43,7 @@ def test_continuity(): f2 = f.get_piece((half, 0)) div_f1 = f1.div() div_f2 = f2.div() - line = ((t[0], t[0])) + line = (t[0], t[0]) f1 = f1.subs(x[:2], line) f2 = f2.subs(x[:2], line) div_f1 = div_f1.subs(x[:2], line) diff --git a/test/test_bell.py b/test/test_bell.py index ced45167..6c1689a3 100644 --- a/test/test_bell.py +++ b/test/test_bell.py @@ -10,8 +10,7 @@ def test_bell_polyset(): gradp = [p.diff(x[0]), p.diff(x[1])] for en in range(b.reference.sub_entity_count(1)): edge = b.reference.sub_entity(1, en) - variables = [o + sum(a[i] * t[0] for a in edge.axes) - for i, o in enumerate(edge.origin)] + variables = [o + sum(a[i] * t[0] for a in edge.axes) for i, o in enumerate(edge.origin)] n = edge.normal() normal_deriv = gradp[0] * n[0] + gradp[1] * n[1] normal_deriv = normal_deriv.subs(x, variables) diff --git a/test/test_bernstein.py b/test/test_bernstein.py index 81e2ed26..86a7eff2 100644 --- a/test/test_bernstein.py +++ b/test/test_bernstein.py @@ -5,9 +5,10 @@ import symfem -@pytest.mark.parametrize("celltype, degree", [(c, i) for c, n in [ - ("interval", 4), ("triangle", 3), ("tetrahedron", 2) -] for i in range(n)]) +@pytest.mark.parametrize( + "celltype, degree", + [(c, i) for c, n in [("interval", 4), ("triangle", 3), ("tetrahedron", 2)] for i in range(n)], +) def test_bernstein(celltype, degree): b = symfem.create_element(celltype, "Bernstein", degree) poly = symfem.elements.bernstein.bernstein_polynomials(degree, b.reference.tdim) diff --git a/test/test_docs.py b/test/test_docs.py index ea159898..6a4bb440 100644 --- a/test/test_docs.py +++ b/test/test_docs.py @@ -19,10 +19,8 @@ outputlines: typing.List[str] = [] codelines: typing.List[str] = [] -if os.path.isfile(os.path.join(os.path.dirname(os.path.realpath(__file__)), - "../docs/index.rst")): - with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), - "../docs/index.rst")) as f: +if os.path.isfile(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../docs/index.rst")): + with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../docs/index.rst")) as f: for line in f: if line.strip() == ".. code-block:: python": code = True @@ -45,10 +43,8 @@ output = False code = False doc_data.append(("\n".join(codelines), "\n".join(outputlines))) -if os.path.isfile(os.path.join(os.path.dirname(os.path.realpath(__file__)), - "../README.md")): - with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), - "../README.md")) as f: +if os.path.isfile(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../README.md")): + with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../README.md")) as f: for line in f: if line.strip() != "": if line.strip() == "```python": @@ -115,8 +111,9 @@ def test_readme_elements(): elementlist[r] = [] elementlist[r].append(e.names[0]) - if not os.path.isfile(os.path.join(os.path.dirname(os.path.realpath(__file__)), - "../docs/index.rst")): + if not os.path.isfile( + os.path.join(os.path.dirname(os.path.realpath(__file__)), "../docs/index.rst") + ): pytest.xfail("Could not find README.md") root = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..") @@ -128,8 +125,7 @@ def test_readme_elements(): cell = r.split("\n")[0].strip().lower() cells.append(cell) lines = r.split("### List of supported elements")[1].strip().split("\n") - elements = [i[2:].split("(alternative names:")[0].strip() - for i in lines if i.strip() != ""] + elements = [i[2:].split("(alternative names:")[0].strip() for i in lines if i.strip() != ""] for line in r.split("\n"): if "(alternative names:" in line: diff --git a/test/test_dof_descriptions.py b/test/test_dof_descriptions.py index d9701ccc..fab196c2 100644 --- a/test/test_dof_descriptions.py +++ b/test/test_dof_descriptions.py @@ -10,13 +10,15 @@ @pytest.mark.parametrize( ("cell_type", "element_type", "order", "kwargs"), - [[reference, element, order, kwargs] - for reference, i in test_elements.items() for element, j in i.items() - for kwargs, k in j for order in k]) -def test_element( - elements_to_test, cells_to_test, cell_type, element_type, order, kwargs, - speed -): + [ + [reference, element, order, kwargs] + for reference, i in test_elements.items() + for element, j in i.items() + for kwargs, k in j + for order in k + ], +) +def test_element(elements_to_test, cells_to_test, cell_type, element_type, order, kwargs, speed): """Run tests for each element.""" if elements_to_test != "ALL" and element_type not in elements_to_test: pytest.skip() diff --git a/test/test_elements.py b/test/test_elements.py index ebc8dfe3..3e86ed3f 100644 --- a/test/test_elements.py +++ b/test/test_elements.py @@ -23,19 +23,17 @@ def test_all_tested(): raise ValueError(f"{e.names[0]} on a {r} is not tested") -@pytest.mark.parametrize("ref, element, order", [ - ("triangle", "Hermite", 4), - ("tetrahedron", "Crouzeix-Raviart", 2) -]) +@pytest.mark.parametrize( + "ref, element, order", [("triangle", "Hermite", 4), ("tetrahedron", "Crouzeix-Raviart", 2)] +) def test_too_high_order(ref, element, order): with pytest.raises(ValueError): symfem.create_element(ref, element, order) -@pytest.mark.parametrize("ref, element, order", [ - ("triangle", "Hermite", 2), - ("tetrahedron", "bubble", 3) -]) +@pytest.mark.parametrize( + "ref, element, order", [("triangle", "Hermite", 2), ("tetrahedron", "bubble", 3)] +) def test_too_low_order(ref, element, order): with pytest.raises(ValueError): symfem.create_element(ref, element, order) @@ -43,13 +41,15 @@ def test_too_low_order(ref, element, order): @pytest.mark.parametrize( ("cell_type", "element_type", "order", "kwargs"), - [[reference, element, order, kwargs] - for reference, i in test_elements.items() for element, j in i.items() - for kwargs, k in j for order in k]) -def test_element( - elements_to_test, cells_to_test, cell_type, element_type, order, kwargs, - speed -): + [ + [reference, element, order, kwargs] + for reference, i in test_elements.items() + for element, j in i.items() + for kwargs, k in j + for order in k + ], +) +def test_element(elements_to_test, cells_to_test, cell_type, element_type, order, kwargs, speed): """Run tests for each element.""" if elements_to_test != "ALL" and element_type not in elements_to_test: pytest.skip() diff --git a/test/test_functions.py b/test/test_functions.py index e7b82005..55ebfbb6 100644 --- a/test/test_functions.py +++ b/test/test_functions.py @@ -125,8 +125,12 @@ def test_matrix_function_neg(): m3 = [[1, 2], [0, 1]] m3_neg = [[-1, -2], [0, -1]] a = [m3, MatrixFunction(m3), sympy.Matrix(m3), MatrixFunction(sympy.Matrix(m3))] - a_neg = [m3_neg, MatrixFunction(m3_neg), sympy.Matrix(m3_neg), - MatrixFunction(sympy.Matrix(m3_neg))] + a_neg = [ + m3_neg, + MatrixFunction(m3_neg), + sympy.Matrix(m3_neg), + MatrixFunction(sympy.Matrix(m3_neg)), + ] for i in a: for j in a_neg: if isinstance(i, MatrixFunction) or isinstance(j, MatrixFunction): @@ -141,26 +145,15 @@ def test_matrix_function_subs(): def test_piecewise_scalar_function_add_sub(): - f1 = PiecewiseFunction({ - ((0, 0), (1, 0), (0, 1)): x[0], - ((1, 0), (1, 1), (0, 1)): x[1] - }, 2) - f2 = PiecewiseFunction({ - ((0, 0), (1, 0), (0, 1)): 0, - ((1, 0), (1, 1), (0, 1)): x[0] - }, 2) - f3 = PiecewiseFunction({ - ((0, 0), (1, 0), (0, 1)): x[0], - ((1, 0), (1, 1), (0, 1)): x[0] + x[1] - }, 2) - f4 = PiecewiseFunction({ - ((0, 0), (1, 0), (0, 1)): x[1], - ((1, 0), (1, 1), (0, 1)): x[0] + x[1] - }, 2) - x1_pw = PiecewiseFunction({ - ((0, 0), (1, 0), (0, 1)): x[1], - ((1, 0), (1, 1), (0, 1)): x[1] - }, 2) + f1 = PiecewiseFunction({((0, 0), (1, 0), (0, 1)): x[0], ((1, 0), (1, 1), (0, 1)): x[1]}, 2) + f2 = PiecewiseFunction({((0, 0), (1, 0), (0, 1)): 0, ((1, 0), (1, 1), (0, 1)): x[0]}, 2) + f3 = PiecewiseFunction( + {((0, 0), (1, 0), (0, 1)): x[0], ((1, 0), (1, 1), (0, 1)): x[0] + x[1]}, 2 + ) + f4 = PiecewiseFunction( + {((0, 0), (1, 0), (0, 1)): x[1], ((1, 0), (1, 1), (0, 1)): x[0] + x[1]}, 2 + ) + x1_pw = PiecewiseFunction({((0, 0), (1, 0), (0, 1)): x[1], ((1, 0), (1, 1), (0, 1)): x[1]}, 2) assert f1 + f2 == f3 assert f3 - f1 == f2 @@ -173,19 +166,10 @@ def test_piecewise_scalar_function_add_sub(): def test_piecewise_scalar_function_mult_div(): - f2 = PiecewiseFunction({ - ((0, 0), (1, 0), (0, 1)): 0, - ((1, 0), (1, 1), (0, 1)): x[0] - }, 2) - f5 = PiecewiseFunction({ - ((0, 0), (1, 0), (0, 1)): 0, - ((1, 0), (1, 1), (0, 1)): 2 * x[0] - }, 2) - - two_pw = PiecewiseFunction({ - ((0, 0), (1, 0), (0, 1)): 2, - ((1, 0), (1, 1), (0, 1)): 2 - }, 2) + f2 = PiecewiseFunction({((0, 0), (1, 0), (0, 1)): 0, ((1, 0), (1, 1), (0, 1)): x[0]}, 2) + f5 = PiecewiseFunction({((0, 0), (1, 0), (0, 1)): 0, ((1, 0), (1, 1), (0, 1)): 2 * x[0]}, 2) + + two_pw = PiecewiseFunction({((0, 0), (1, 0), (0, 1)): 2, ((1, 0), (1, 1), (0, 1)): 2}, 2) for two in [2, ScalarFunction(2), two_pw]: assert f2 * two == f5 @@ -193,27 +177,15 @@ def test_piecewise_scalar_function_mult_div(): def test_piecewise_scalar_function_neg(): - f2 = PiecewiseFunction({ - ((0, 0), (1, 0), (0, 1)): 0, - ((1, 0), (1, 1), (0, 1)): x[0] - }, 2) - f6 = PiecewiseFunction({ - ((0, 0), (1, 0), (0, 1)): 0, - ((1, 0), (1, 1), (0, 1)): -x[0] - }, 2) + f2 = PiecewiseFunction({((0, 0), (1, 0), (0, 1)): 0, ((1, 0), (1, 1), (0, 1)): x[0]}, 2) + f6 = PiecewiseFunction({((0, 0), (1, 0), (0, 1)): 0, ((1, 0), (1, 1), (0, 1)): -x[0]}, 2) assert f6 == -f2 def test_piecewise_scalar_function_subs(): - f2 = PiecewiseFunction({ - ((0, 0), (1, 0), (0, 1)): 0, - ((1, 0), (1, 1), (0, 1)): x[0] - }, 2) - f7 = PiecewiseFunction({ - ((0, 0), (1, 0), (0, 1)): 0, - ((1, 0), (1, 1), (0, 1)): 2 - }, 2) + f2 = PiecewiseFunction({((0, 0), (1, 0), (0, 1)): 0, ((1, 0), (1, 1), (0, 1)): x[0]}, 2) + f7 = PiecewiseFunction({((0, 0), (1, 0), (0, 1)): 0, ((1, 0), (1, 1), (0, 1)): 2}, 2) assert f2.subs(x[0], 2) == f7 assert f2.subs(x[:2], (1, 1)) == 1 diff --git a/test/test_guzman_neilan.py b/test/test_guzman_neilan.py index e1d26e22..5c70aabf 100644 --- a/test/test_guzman_neilan.py +++ b/test/test_guzman_neilan.py @@ -71,8 +71,9 @@ def test_basis_continuity_tetrahedron(order): if value is None: value = p[1].subs(x, pt) assert value == p[1].subs(x, pt) - for pts in combinations([(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1), - (quarter, quarter, quarter)], 2): + for pts in combinations( + [(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1), (quarter, quarter, quarter)], 2 + ): for i in range(N + 1): pt = tuple(a + (b - a) * i * one / N for a, b in zip(*pts)) for f in e.get_polynomial_basis(): @@ -82,12 +83,14 @@ def test_basis_continuity_tetrahedron(order): if value is None: value = p[1].subs(x, pt) assert value == p[1].subs(x, pt) - for pts in combinations([(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1), - (quarter, quarter, quarter)], 3): + for pts in combinations( + [(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1), (quarter, quarter, quarter)], 3 + ): for i in range(N + 1): for j in range(N + 1 - i): - pt = tuple(a + (b - a) * i * one / N + (c - a) * j * one / N - for a, b, c in zip(*pts)) + pt = tuple( + a + (b - a) * i * one / N + (c - a) * j * one / N for a, b, c in zip(*pts) + ) for f in e.get_polynomial_basis(): value = None for p in f.pieces.items(): @@ -104,14 +107,15 @@ def test_piecewise_lagrange_triangle(order): sub_tris = [ (reference.vertices[0], reference.vertices[1], mid), (reference.vertices[0], reference.vertices[2], mid), - (reference.vertices[1], reference.vertices[2], mid)] + (reference.vertices[1], reference.vertices[2], mid), + ] N = 5 fs = make_piecewise_lagrange(sub_tris, "triangle", order, zero_on_boundary=True) for e_n in range(reference.sub_entity_count(1)): edge = reference.sub_entity(1, e_n) for i in range(N + 1): - point = edge.get_point((sympy.Rational(i, N), )) + point = edge.get_point((sympy.Rational(i, N),)) for f in fs: assert f.subs(x, point) == (0, 0) @@ -128,7 +132,8 @@ def test_piecewise_lagrange_tetrahedron(order): (reference.vertices[0], reference.vertices[1], reference.vertices[2], mid), (reference.vertices[0], reference.vertices[1], reference.vertices[3], mid), (reference.vertices[0], reference.vertices[2], reference.vertices[3], mid), - (reference.vertices[1], reference.vertices[2], reference.vertices[3], mid)] + (reference.vertices[1], reference.vertices[2], reference.vertices[3], mid), + ] N = 5 fs = make_piecewise_lagrange(sub_tets, "tetrahedron", order, zero_on_boundary=True) @@ -136,8 +141,7 @@ def test_piecewise_lagrange_tetrahedron(order): face = reference.sub_entity(2, f_n) for i in range(N + 1): for j in range(N + 1 - i): - point = face.get_point((sympy.Rational(i, N), - sympy.Rational(j, N))) + point = face.get_point((sympy.Rational(i, N), sympy.Rational(j, N))) for f in fs: assert f.subs(x, point) == (0, 0, 0) diff --git a/test/test_hct.py b/test/test_hct.py index a216c747..83b9212f 100644 --- a/test/test_hct.py +++ b/test/test_hct.py @@ -20,7 +20,7 @@ def test_c1_continuity(family): f2 = f.get_piece((half, half)) grad_f1 = f1.grad(2) grad_f2 = f2.grad(2) - line = ((1 - 2 * t[0], t[0])) + line = (1 - 2 * t[0], t[0]) f1 = f1.subs(x[:2], line) f2 = f2.subs(x[:2], line) grad_f1 = grad_f1.subs(x[:2], line) @@ -33,7 +33,7 @@ def test_c1_continuity(family): f2 = f.get_piece((0, half)) grad_f1 = f1.grad(2) grad_f2 = f2.grad(2) - line = ((t[0], 1 - 2 * t[0])) + line = (t[0], 1 - 2 * t[0]) f1 = f1.subs(x[:2], line) f2 = f2.subs(x[:2], line) grad_f1 = grad_f1.subs(x[:2], line) @@ -46,7 +46,7 @@ def test_c1_continuity(family): f2 = f.get_piece((half, 0)) grad_f1 = f1.grad(2) grad_f2 = f2.grad(2) - line = ((t[0], t[0])) + line = (t[0], t[0]) f1 = f1.subs(x[:2], line) f2 = f2.subs(x[:2], line) grad_f1 = grad_f1.subs(x[:2], line) @@ -82,5 +82,6 @@ def test_rhct_integral(): expr = integrand.pieces[((0, 1), (0, 0), (third, third))].as_sympy() assert len(integrand.pieces) == 3 - assert sympy.integrate(sympy.integrate( - expr, (x[1], x[0], 1 - 2 * x[0])), (x[0], 0, third)) == integrand.integral(ref, x) + assert sympy.integrate( + sympy.integrate(expr, (x[1], x[0], 1 - 2 * x[0])), (x[0], 0, third) + ) == integrand.integral(ref, x) diff --git a/test/test_hellan_herrmann_johnson.py b/test/test_hellan_herrmann_johnson.py index 3dd0f686..8476ada0 100644 --- a/test/test_hellan_herrmann_johnson.py +++ b/test/test_hellan_herrmann_johnson.py @@ -8,10 +8,9 @@ from symfem.utils import allequal -@pytest.mark.parametrize('reference', ['triangle', 'tetrahedron']) -@pytest.mark.parametrize('order', [0, 1, 2]) +@pytest.mark.parametrize("reference", ["triangle", "tetrahedron"]) +@pytest.mark.parametrize("order", [0, 1, 2]) def test_create(reference, order): - element = create_element(reference, "HHJ", order) # Get the basis functions associated with the interior diff --git a/test/test_lagrange_polynomial_variants.py b/test/test_lagrange_polynomial_variants.py index e29271a3..046fc78c 100644 --- a/test/test_lagrange_polynomial_variants.py +++ b/test/test_lagrange_polynomial_variants.py @@ -3,19 +3,37 @@ import symfem -@pytest.mark.parametrize("cell, order", [ - (c, o) for c, m in [("interval", 5), ("triangle", 4), ("quadrilateral", 4), ("tetrahedron", 3), - ("hexahedron", 3), ("prism", 3), ("pyramid", 3)] for o in range(m)]) +@pytest.mark.parametrize( + "cell, order", + [ + (c, o) + for c, m in [ + ("interval", 5), + ("triangle", 4), + ("quadrilateral", 4), + ("tetrahedron", 3), + ("hexahedron", 3), + ("prism", 3), + ("pyramid", 3), + ] + for o in range(m) + ], +) def test_legendre(cell, order): basis = symfem.polynomials.orthonormal_basis(cell, order, 0)[0] e = symfem.create_element(cell, "P", order, variant="legendre") assert symfem.utils.allequal(e.get_basis_functions(), basis) -@pytest.mark.parametrize("cell, order", [ - (c, o) for c, m in [("interval", 5), ("quadrilateral", 4), ("hexahedron", 3)] - for o in range(m)]) +@pytest.mark.parametrize( + "cell, order", + [ + (c, o) + for c, m in [("interval", 5), ("quadrilateral", 4), ("hexahedron", 3)] + for o in range(m) + ], +) def test_lobatto(cell, order): basis = symfem.polynomials.lobatto_basis(cell, order, False) e = symfem.create_element(cell, "P", order, variant="lobatto") - assert symfem.utils.allequal(e.get_basis_functions()[-len(basis):], basis) + assert symfem.utils.allequal(e.get_basis_functions()[-len(basis) :], basis) diff --git a/test/test_mapping.py b/test/test_mapping.py index 34ec5a5f..953e3399 100644 --- a/test/test_mapping.py +++ b/test/test_mapping.py @@ -10,12 +10,16 @@ @pytest.mark.parametrize( ("cell_type", "element_type", "order", "kwargs"), - [[reference, element, order, kwargs] - for reference, i in test_elements.items() for element, j in i.items() - for kwargs, k in j for order in k]) + [ + [reference, element, order, kwargs] + for reference, i in test_elements.items() + for element, j in i.items() + for kwargs, k in j + for order in k + ], +) def test_push_forward( - elements_to_test, cells_to_test, cell_type, element_type, order, kwargs, - speed + elements_to_test, cells_to_test, cell_type, element_type, order, kwargs, speed ): if elements_to_test != "ALL" and element_type not in elements_to_test: pytest.skip() @@ -28,7 +32,7 @@ def test_push_forward( pytest.skip() if cell_type == "interval": - vertices = [(3, ), (1, )] + vertices = [(3,), (1,)] elif cell_type == "triangle": vertices = [(1, 1), (2, 2), (1, 4)] elif cell_type == "quadrilateral": @@ -36,8 +40,16 @@ def test_push_forward( elif cell_type == "tetrahedron": vertices = [(1, 1, 1), (2, 2, 2), (-1, 3, 2), (4, 0, 0)] elif cell_type == "hexahedron": - vertices = [(1, 1, 1), (2, 2, 2), (-1, 3, 2), (0, 4, 3), - (4, 0, 0), (5, 1, 1), (2, 2, 1), (3, 3, 2)] + vertices = [ + (1, 1, 1), + (2, 2, 2), + (-1, 3, 2), + (0, 4, 3), + (4, 0, 0), + (5, 1, 1), + (2, 2, 1), + (3, 3, 2), + ] elif cell_type == "prism": vertices = [(1, 1, 1), (2, 2, 1), (1, 4, 2), (0, 1, 1), (1, 2, 1), (0, 4, 2)] elif cell_type == "pyramid": @@ -58,23 +70,26 @@ def test_push_forward( pytest.xfail("Mapping not implemented for this element.") -@pytest.mark.parametrize("name, inverse, transpose, mapping", [ - ("identity", False, False, symfem.mappings.identity), - ("l2", False, False, symfem.mappings.l2), - ("covariant", False, False, symfem.mappings.covariant), - ("contravariant", False, False, symfem.mappings.contravariant), - ("double_covariant", False, False, symfem.mappings.double_covariant), - ("double_contravariant", False, False, symfem.mappings.double_contravariant), - ("identity", True, True, symfem.mappings.identity_inverse_transpose), - ("l2", True, True, symfem.mappings.l2_inverse_transpose), - ("covariant", True, True, symfem.mappings.covariant_inverse_transpose), - ("contravariant", True, True, symfem.mappings.contravariant_inverse_transpose), - ("identity", True, False, symfem.mappings.identity_inverse), - ("l2", True, False, symfem.mappings.l2_inverse), - ("covariant", True, False, symfem.mappings.covariant_inverse), - ("contravariant", True, False, symfem.mappings.contravariant_inverse), - ("double_covariant", True, False, symfem.mappings.double_covariant_inverse), - ("double_contravariant", True, False, symfem.mappings.double_contravariant_inverse), -]) +@pytest.mark.parametrize( + "name, inverse, transpose, mapping", + [ + ("identity", False, False, symfem.mappings.identity), + ("l2", False, False, symfem.mappings.l2), + ("covariant", False, False, symfem.mappings.covariant), + ("contravariant", False, False, symfem.mappings.contravariant), + ("double_covariant", False, False, symfem.mappings.double_covariant), + ("double_contravariant", False, False, symfem.mappings.double_contravariant), + ("identity", True, True, symfem.mappings.identity_inverse_transpose), + ("l2", True, True, symfem.mappings.l2_inverse_transpose), + ("covariant", True, True, symfem.mappings.covariant_inverse_transpose), + ("contravariant", True, True, symfem.mappings.contravariant_inverse_transpose), + ("identity", True, False, symfem.mappings.identity_inverse), + ("l2", True, False, symfem.mappings.l2_inverse), + ("covariant", True, False, symfem.mappings.covariant_inverse), + ("contravariant", True, False, symfem.mappings.contravariant_inverse), + ("double_covariant", True, False, symfem.mappings.double_covariant_inverse), + ("double_contravariant", True, False, symfem.mappings.double_contravariant_inverse), + ], +) def test_get_mapping(name, inverse, transpose, mapping): assert symfem.mappings.get_mapping(name, inverse=inverse, transpose=transpose) == mapping diff --git a/test/test_maximum_degree.py b/test/test_maximum_degree.py index fa014ffb..4d44d7b9 100644 --- a/test/test_maximum_degree.py +++ b/test/test_maximum_degree.py @@ -3,8 +3,10 @@ import symfem -@pytest.mark.parametrize("cell", ["interval", "triangle", "tetrahedron", "quadrilateral", - "hexahedron", "prism", "pyramid"]) +@pytest.mark.parametrize( + "cell", + ["interval", "triangle", "tetrahedron", "quadrilateral", "hexahedron", "prism", "pyramid"], +) @pytest.mark.parametrize("order", range(1, 4)) def test_lagrange(cell, order): e = symfem.create_element(cell, "P", order) diff --git a/test/test_nedelec.py b/test/test_nedelec.py index ad8f33da..fcd3d9df 100644 --- a/test/test_nedelec.py +++ b/test/test_nedelec.py @@ -12,16 +12,13 @@ def test_nedelec_2d(): tdim = 2 - for i, edge in enumerate([ - ((1, 0), (0, 1)), - ((0, 0), (0, 1)), - ((0, 0), (1, 0)) - ]): + for i, edge in enumerate([((1, 0), (0, 1)), ((0, 0), (0, 1)), ((0, 0), (1, 0))]): for j, f in enumerate(space.get_basis_functions()): norm = sympy.sqrt(sum((edge[0][i] - edge[1][i]) ** 2 for i in range(tdim))) tangent = tuple((edge[1][i] - edge[0][i]) / norm for i in range(tdim)) - line = sympy.Curve([(1 - k) * edge[0][i] + k * edge[1][i] for i in range(tdim)], - (k, 0, 1)) + line = sympy.Curve( + [(1 - k) * edge[0][i] + k * edge[1][i] for i in range(tdim)], (k, 0, 1) + ) result = sympy.line_integrate(f.dot(tangent), line, x[:tdim]) if i == j: @@ -36,14 +33,16 @@ def test_nedelec_3d(): tdim = 3 - for i, edge in enumerate([ - ((0, 1, 0), (0, 0, 1)), - ((1, 0, 0), (0, 0, 1)), - ((1, 0, 0), (0, 1, 0)), - ((0, 0, 0), (0, 0, 1)), - ((0, 0, 0), (0, 1, 0)), - ((0, 0, 0), (1, 0, 0)) - ]): + for i, edge in enumerate( + [ + ((0, 1, 0), (0, 0, 1)), + ((1, 0, 0), (0, 0, 1)), + ((1, 0, 0), (0, 1, 0)), + ((0, 0, 0), (0, 0, 1)), + ((0, 0, 0), (0, 1, 0)), + ((0, 0, 0), (1, 0, 0)), + ] + ): for j, f in enumerate(space.get_basis_functions()): norm = sympy.sqrt(sum((edge[0][i] - edge[1][i]) ** 2 for i in range(tdim))) tangent = tuple((edge[1][i] - edge[0][i]) / norm for i in range(tdim)) diff --git a/test/test_plotting.py b/test/test_plotting.py index cabf6776..cdb206f0 100644 --- a/test/test_plotting.py +++ b/test/test_plotting.py @@ -27,8 +27,7 @@ def compile_tex(filename): def test_plot_line(): p = symfem.plotting.Picture() - p.add_line((sympy.Integer(0), sympy.Integer(0)), - (sympy.Integer(1), sympy.Integer(1)), "blue") + p.add_line((sympy.Integer(0), sympy.Integer(0)), (sympy.Integer(1), sympy.Integer(1)), "blue") for ext in ["svg", "png", "tex"]: p.save(os.path.join(folder, f"test_plot_line.{ext}")) compile_tex("test_plot_line.tex") @@ -36,8 +35,9 @@ def test_plot_line(): def test_plot_arrow(): p = symfem.plotting.Picture() - p.add_arrow((sympy.Integer(0), sympy.Integer(0)), - (sympy.Integer(1), sympy.Integer(1)), "orange") + p.add_arrow( + (sympy.Integer(0), sympy.Integer(0)), (sympy.Integer(1), sympy.Integer(1)), "orange" + ) for ext in ["svg", "png", "tex"]: p.save(os.path.join(folder, f"test_plot_arrow.{ext}")) compile_tex("test_plot_arrow.tex") @@ -53,19 +53,25 @@ def test_plot_ncircle(): def test_plot_fill(): p = symfem.plotting.Picture() - p.add_fill(( - (sympy.Integer(0), sympy.Integer(0)), (sympy.Integer(1), sympy.Integer(1)), - (sympy.Integer(1), sympy.Rational(1, 2)), (sympy.Rational(1, 2), sympy.Integer(0)) - ), "purple", 0.5) + p.add_fill( + ( + (sympy.Integer(0), sympy.Integer(0)), + (sympy.Integer(1), sympy.Integer(1)), + (sympy.Integer(1), sympy.Rational(1, 2)), + (sympy.Rational(1, 2), sympy.Integer(0)), + ), + "purple", + 0.5, + ) for ext in ["svg", "png", "tex"]: p.save(os.path.join(folder, f"test_plot_fill.{ext}")) compile_tex("test_plot_fill.tex") -@pytest.mark.parametrize("reference", [ - "interval", "triangle", "quadrilateral", - "tetrahedron", "hexahedron", "prism", "pyramid" -]) +@pytest.mark.parametrize( + "reference", + ["interval", "triangle", "quadrilateral", "tetrahedron", "hexahedron", "prism", "pyramid"], +) def test_z_ordering(reference): r = symfem.create_reference(reference) @@ -79,23 +85,29 @@ def test_z_ordering(reference): assert len([e for e in z if e[0] == i]) == r.sub_entity_count(i) -@pytest.mark.parametrize("reference", [ - "interval", "triangle", "quadrilateral", - "tetrahedron", "hexahedron", "prism", "pyramid" -]) +@pytest.mark.parametrize( + "reference", + ["interval", "triangle", "quadrilateral", "tetrahedron", "hexahedron", "prism", "pyramid"], +) @pytest.mark.parametrize("degree", [0, 2, 5]) def test_dof_diagrams_lagrange(reference, degree): e = symfem.create_element(reference, "P", degree) for ext in ["svg", "png", "tex"]: - e.plot_dof_diagram(os.path.join( - folder, f"test_dof_diagrams_lagrange-{reference}-{degree}.{ext}")) + e.plot_dof_diagram( + os.path.join(folder, f"test_dof_diagrams_lagrange-{reference}-{degree}.{ext}") + ) compile_tex(f"test_dof_diagrams_lagrange-{reference}-{degree}.tex") -@pytest.mark.parametrize("reference", [ - "triangle", "quadrilateral", - "tetrahedron", "hexahedron", -]) +@pytest.mark.parametrize( + "reference", + [ + "triangle", + "quadrilateral", + "tetrahedron", + "hexahedron", + ], +) @pytest.mark.parametrize("degree", [1, 2]) def test_dof_diagrams_raviart_thomas(reference, degree): if reference in ["quadrilateral", "hexahedron"]: @@ -103,40 +115,57 @@ def test_dof_diagrams_raviart_thomas(reference, degree): else: e = symfem.create_element(reference, "RT", degree) for ext in ["svg", "png", "tex"]: - e.plot_dof_diagram(os.path.join( - folder, f"test_dof_diagrams_raviart_thomas-{reference}-{degree}.{ext}")) + e.plot_dof_diagram( + os.path.join(folder, f"test_dof_diagrams_raviart_thomas-{reference}-{degree}.{ext}") + ) compile_tex(f"test_dof_diagrams_raviart_thomas-{reference}-{degree}.tex") -@pytest.mark.parametrize("reference", [ - "interval", "triangle", "quadrilateral", - "tetrahedron", "hexahedron", -]) +@pytest.mark.parametrize( + "reference", + [ + "interval", + "triangle", + "quadrilateral", + "tetrahedron", + "hexahedron", + ], +) @pytest.mark.parametrize("degree", [1, 2]) def test_dof_diagrams_eg(reference, degree): e = symfem.create_element(reference, "EG", degree) for ext in ["svg", "png", "tex"]: - e.plot_dof_diagram(os.path.join( - folder, f"test_dof_diagrams_eg-{reference}-{degree}.{ext}")) + e.plot_dof_diagram(os.path.join(folder, f"test_dof_diagrams_eg-{reference}-{degree}.{ext}")) compile_tex(f"test_dof_diagrams_eg-{reference}-{degree}.tex") -@pytest.mark.parametrize("reference", [ - "interval", "triangle", "quadrilateral", -]) +@pytest.mark.parametrize( + "reference", + [ + "interval", + "triangle", + "quadrilateral", + ], +) @pytest.mark.parametrize("degree", [0, 1, 2]) def test_function_plots_lagrange(reference, degree): e = symfem.create_element(reference, "P", degree) for ext in ["svg", "png", "tex"]: - e.plot_basis_function(0, os.path.join( - folder, f"test_function_plots_lagrange-{reference}-{degree}.{ext}")) + e.plot_basis_function( + 0, os.path.join(folder, f"test_function_plots_lagrange-{reference}-{degree}.{ext}") + ) compile_tex(f"test_function_plots_lagrange-{reference}-{degree}.tex") -@pytest.mark.parametrize("reference", [ - "triangle", "quadrilateral", - "tetrahedron", "hexahedron", -]) +@pytest.mark.parametrize( + "reference", + [ + "triangle", + "quadrilateral", + "tetrahedron", + "hexahedron", + ], +) @pytest.mark.parametrize("degree", [1]) def test_function_plots_raviart_thomas(reference, degree): if reference in ["quadrilateral", "hexahedron"]: @@ -144,30 +173,42 @@ def test_function_plots_raviart_thomas(reference, degree): else: e = symfem.create_element(reference, "RT", degree) for ext in ["svg", "png", "tex"]: - e.plot_basis_function(0, os.path.join( - folder, f"test_function_plots_raviart_thomas-{reference}-{degree}.{ext}")) + e.plot_basis_function( + 0, + os.path.join(folder, f"test_function_plots_raviart_thomas-{reference}-{degree}.{ext}"), + ) compile_tex(f"test_function_plots_raviart_thomas-{reference}-{degree}.tex") -@pytest.mark.parametrize("reference", [ - "triangle", "quadrilateral", -]) +@pytest.mark.parametrize( + "reference", + [ + "triangle", + "quadrilateral", + ], +) def test_function_plots_piecewise_scalar(reference): e = symfem.create_element(reference, "P1-iso-P2", 1) for ext in ["svg", "png", "tex"]: - e.plot_basis_function(0, os.path.join( - folder, f"test_function_plots_piecewise_scalar-{reference}.{ext}")) + e.plot_basis_function( + 0, os.path.join(folder, f"test_function_plots_piecewise_scalar-{reference}.{ext}") + ) compile_tex(f"test_function_plots_piecewise_scalar-{reference}.tex") -@pytest.mark.parametrize("reference", [ - "triangle", "tetrahedron", -]) +@pytest.mark.parametrize( + "reference", + [ + "triangle", + "tetrahedron", + ], +) def test_function_plots_piecewise_vector(reference): e = symfem.create_element(reference, "Guzman-Neilan", 1) for ext in ["svg", "png", "tex"]: - e.plot_basis_function(0, os.path.join( - folder, f"test_function_plots_piecewise_vector-{reference}.{ext}")) + e.plot_basis_function( + 0, os.path.join(folder, f"test_function_plots_piecewise_vector-{reference}.{ext}") + ) compile_tex(f"test_function_plots_piecewise_vector-{reference}.tex") @@ -175,8 +216,7 @@ def test_function_plots_piecewise_vector(reference): def test_function_plots_dual(n): e = symfem.create_element(f"dual polygon({n})", "dual", 1) for ext in ["svg", "png", "tex"]: - e.plot_basis_function(0, os.path.join( - folder, f"test_function_plots_dual-{n}.{ext}")) + e.plot_basis_function(0, os.path.join(folder, f"test_function_plots_dual-{n}.{ext}")) compile_tex(f"test_function_plots_dual-{n}.tex") @@ -184,63 +224,81 @@ def test_function_plots_dual(n): def test_function_plots_bc(n): e = symfem.create_element(f"dual polygon({n})", "BC", 1) for ext in ["svg", "png", "tex"]: - e.plot_basis_function(0, os.path.join( - folder, f"test_function_plots_bc-{n}.{ext}")) + e.plot_basis_function(0, os.path.join(folder, f"test_function_plots_bc-{n}.{ext}")) compile_tex(f"test_function_plots_bc-{n}.tex") -@pytest.mark.parametrize("reference", [ - "interval", "triangle", "quadrilateral", -]) +@pytest.mark.parametrize( + "reference", + [ + "interval", + "triangle", + "quadrilateral", + ], +) @pytest.mark.parametrize("degree", [1, 2]) def test_function_plots_eg(reference, degree): e = symfem.create_element(reference, "EG", degree) for ext in ["svg", "png", "tex"]: - e.plot_basis_function(0, os.path.join( - folder, f"test_function_plots_eg-{reference}-{degree}.{ext}")) + e.plot_basis_function( + 0, os.path.join(folder, f"test_function_plots_eg-{reference}-{degree}.{ext}") + ) compile_tex(f"test_function_plots_eg-{reference}-{degree}.tex") -@pytest.mark.parametrize("reference", [ - "interval", "triangle", "quadrilateral", - "tetrahedron", "hexahedron", "prism", "pyramid", - "dual polygon(6)" -]) +@pytest.mark.parametrize( + "reference", + [ + "interval", + "triangle", + "quadrilateral", + "tetrahedron", + "hexahedron", + "prism", + "pyramid", + "dual polygon(6)", + ], +) def test_plot_reference(reference): r = symfem.create_reference(reference) rname = reference.replace("(", "").replace(")", "").replace(" ", "_") for ext in ["svg", "png", "tex"]: - r.plot_entity_diagrams(os.path.join( - folder, f"test_plot_reference-{rname}.{ext}")) + r.plot_entity_diagrams(os.path.join(folder, f"test_plot_reference-{rname}.{ext}")) compile_tex(f"test_plot_reference-{rname}.tex") def test_metadata(): - img = symfem.plotting.Picture(svg_metadata=( - "\n" - " \n" - " \n" - " Title\n" - " 1970-01-01\n" - " \n" - " Symfem\n" - " \n" - " See document description\n" - " \n" - " image/svg+xml\n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - "\n")) - img.add_line((sympy.Integer(0), sympy.Integer(0)), - (sympy.Integer(1), sympy.Integer(1)), symfem.plotting.colors.ORANGE) + img = symfem.plotting.Picture( + svg_metadata=( + "\n" + " \n" + " \n" + " Title\n" + " 1970-01-01\n" + " \n" + " Symfem\n" + " \n" + " See document description\n" + " \n" + " image/svg+xml\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n" + ) + ) + img.add_line( + (sympy.Integer(0), sympy.Integer(0)), + (sympy.Integer(1), sympy.Integer(1)), + symfem.plotting.colors.ORANGE, + ) img.save(os.path.join(folder, "test_metadata.svg")) diff --git a/test/test_polynomials.py b/test/test_polynomials.py index 13397e45..72ee11a4 100644 --- a/test/test_polynomials.py +++ b/test/test_polynomials.py @@ -7,8 +7,13 @@ from symfem import create_element, create_reference from symfem.functions import VectorFunction -from symfem.polynomials import (Hcurl_polynomials, Hdiv_polynomials, l2_dual, orthogonal_basis, - orthonormal_basis) +from symfem.polynomials import ( + Hcurl_polynomials, + Hdiv_polynomials, + l2_dual, + orthogonal_basis, + orthonormal_basis, +) from symfem.symbols import t, x @@ -28,7 +33,7 @@ def test_Hcurl_space(reference, order): ref = create_reference(reference) polynomials = Hcurl_polynomials(ref.tdim, ref.tdim, order) for p in polynomials: - assert p.dot(VectorFunction(x[:ref.tdim])) == 0 + assert p.dot(VectorFunction(x[: ref.tdim])) == 0 @pytest.mark.parametrize("reference", ["triangle"]) @@ -38,16 +43,15 @@ def test_MTW_space(reference): for p in polynomials: assert p.div().as_sympy().is_real for vs in e.reference.sub_entities(1): - sub_ref = create_reference(e.reference.sub_entity_types[1], - [e.reference.vertices[i] for i in vs]) - p_edge = p.subs(x, [i + t[0] * j - for i, j in zip(sub_ref.origin, sub_ref.axes[0])]) + sub_ref = create_reference( + e.reference.sub_entity_types[1], [e.reference.vertices[i] for i in vs] + ) + p_edge = p.subs(x, [i + t[0] * j for i, j in zip(sub_ref.origin, sub_ref.axes[0])]) poly = p_edge.dot(sub_ref.normal()).as_sympy().expand().simplify() assert poly.is_real or sympy.Poly(poly).degree() <= 1 -@pytest.mark.parametrize("reference", ["triangle", "quadrilateral", - "tetrahedron", "hexahedron"]) +@pytest.mark.parametrize("reference", ["triangle", "quadrilateral", "tetrahedron", "hexahedron"]) @pytest.mark.parametrize("order", range(1, 5)) def test_BDFM_space(reference, order): e = create_element(reference, "BDFM", order) @@ -55,23 +59,27 @@ def test_BDFM_space(reference, order): tdim = e.reference.tdim for p in polynomials: for vs in e.reference.sub_entities(tdim - 1): - sub_ref = create_reference(e.reference.sub_entity_types[tdim - 1], - [e.reference.vertices[i] for i in vs]) + sub_ref = create_reference( + e.reference.sub_entity_types[tdim - 1], [e.reference.vertices[i] for i in vs] + ) if tdim == 2: - p_edge = p.subs(x, [i + t[0] * j - for i, j in zip(sub_ref.origin, sub_ref.axes[0])]) + p_edge = p.subs(x, [i + t[0] * j for i, j in zip(sub_ref.origin, sub_ref.axes[0])]) else: - p_edge = p.subs(x, [i + t[0] * j + t[1] * k - for i, j, k in zip(sub_ref.origin, sub_ref.axes[0], - sub_ref.axes[1])]) + p_edge = p.subs( + x, + [ + i + t[0] * j + t[1] * k + for i, j, k in zip(sub_ref.origin, sub_ref.axes[0], sub_ref.axes[1]) + ], + ) poly = p_edge.dot(sub_ref.normal()).as_sympy().expand().simplify() assert poly.is_real or sympy.Poly(poly).degree() <= order - 1 -@pytest.mark.parametrize("reference", [ - "interval", "triangle", "quadrilateral", - "tetrahedron", "hexahedron", "prism", - "pyramid"]) +@pytest.mark.parametrize( + "reference", + ["interval", "triangle", "quadrilateral", "tetrahedron", "hexahedron", "prism", "pyramid"], +) @pytest.mark.parametrize("order", range(5)) def test_orthogonal_polynomials(reference, order, speed): if speed == "fast" and order > 2: @@ -91,13 +99,10 @@ def test_orthogonal_polynomials(reference, order, speed): assert (p * q).integral(ref, x) == 0 -@pytest.mark.parametrize("reference", [ - "interval", - "triangle", "quadrilateral", - "tetrahedron", - "hexahedron", "prism", - "pyramid" -]) +@pytest.mark.parametrize( + "reference", + ["interval", "triangle", "quadrilateral", "tetrahedron", "hexahedron", "prism", "pyramid"], +) @pytest.mark.parametrize("order", range(5)) def test_orthogonal_polynomial_derivatives(reference, order): polynomials = orthogonal_basis(reference, order, 2) @@ -123,10 +128,10 @@ def test_orthogonal_polynomial_derivatives(reference, order): assert p.diff(x[j]).diff(x[k]) == q -@pytest.mark.parametrize("reference", [ - "interval", "triangle", "quadrilateral", - "tetrahedron", "hexahedron", "prism", - "pyramid"]) +@pytest.mark.parametrize( + "reference", + ["interval", "triangle", "quadrilateral", "tetrahedron", "hexahedron", "prism", "pyramid"], +) @pytest.mark.parametrize("order", range(3)) def test_orthonormal_polynomials(reference, order, speed): if speed == "fast" and order > 2: @@ -135,13 +140,25 @@ def test_orthonormal_polynomials(reference, order, speed): polynomials = orthonormal_basis(reference, order, 0)[0] ref = create_reference(reference) for p in polynomials: - assert (p ** 2).integral(ref, x) == 1 - - -@pytest.mark.parametrize("reference, order", [ - (r, o) for r, m in [("interval", 6), ("triangle", 3), ("quadrilateral", 3), - ("tetrahedron", 2), ("hexahedron", 2), ("prism", 2), - ("pyramid", 2)] for o in range(m)]) + assert (p**2).integral(ref, x) == 1 + + +@pytest.mark.parametrize( + "reference, order", + [ + (r, o) + for r, m in [ + ("interval", 6), + ("triangle", 3), + ("quadrilateral", 3), + ("tetrahedron", 2), + ("hexahedron", 2), + ("prism", 2), + ("pyramid", 2), + ] + for o in range(m) + ], +) def test_dual(reference, order): ref = create_reference(reference) poly = create_element(reference, "P", order).get_polynomial_basis() diff --git a/test/test_quadrature.py b/test/test_quadrature.py index 6f8e549a..0ab7e336 100644 --- a/test/test_quadrature.py +++ b/test/test_quadrature.py @@ -16,8 +16,8 @@ def test_equispaced(order): poly = ScalarFunction(x) assert allequal( - poly.integrate((x, 0, 1)), - sum(i * poly.subs(x, j) for i, j in zip(weights, points))) + poly.integrate((x, 0, 1)), sum(i * poly.subs(x, j) for i, j in zip(weights, points)) + ) @pytest.mark.parametrize("order", range(1, 7)) @@ -28,8 +28,8 @@ def test_lobatto(order): poly = ScalarFunction(x ** (2 * order - 1)) assert allequal( - poly.integrate((x, 0, 1)), - sum(i * poly.subs(x, j) for i, j in zip(weights, points))) + poly.integrate((x, 0, 1)), sum(i * poly.subs(x, j) for i, j in zip(weights, points)) + ) @pytest.mark.parametrize("order", range(1, 3)) @@ -40,8 +40,8 @@ def test_radau(order): poly = ScalarFunction(x ** (2 * order - 1)) assert allequal( - poly.integrate((x, 0, 1)), - sum(i * poly.subs(x, j) for i, j in zip(weights, points))) + poly.integrate((x, 0, 1)), sum(i * poly.subs(x, j) for i, j in zip(weights, points)) + ) @pytest.mark.parametrize("order", range(1, 4)) @@ -52,5 +52,5 @@ def test_legendre(order): poly = ScalarFunction(x ** (2 * order - 1)) assert allequal( - poly.integrate((x, 0, 1)), - sum(i * poly.subs(x, j) for i, j in zip(weights, points))) + poly.integrate((x, 0, 1)), sum(i * poly.subs(x, j) for i, j in zip(weights, points)) + ) diff --git a/test/test_references.py b/test/test_references.py index 7ff6d989..683d86b1 100644 --- a/test/test_references.py +++ b/test/test_references.py @@ -7,8 +7,14 @@ @pytest.mark.parametrize( "ReferenceClass", - [references.Interval, references.Triangle, references.Tetrahedron, - references.Quadrilateral, references.Hexahedron]) + [ + references.Interval, + references.Triangle, + references.Tetrahedron, + references.Quadrilateral, + references.Hexahedron, + ], +) def test_reference(ReferenceClass): ref = ReferenceClass() assert ref.jacobian() == 1 @@ -16,12 +22,26 @@ def test_reference(ReferenceClass): @pytest.mark.parametrize( ("ReferenceClass", "points"), - [(references.Interval, [(1, 0), (1, 2)]), - (references.Triangle, [(1, 0), (3, 0), (1, 1)]), - (references.Tetrahedron, [(1, 0, 1), (2, 0, 1), (1, 1, 1), (1, 0, 3)]), - (references.Quadrilateral, [(1, 0), (3, 0), (1, 1), (3, 1)]), - (references.Hexahedron, [(1, 0, 1), (2, 0, 1), (1, 1, 1), (2, 1, 1), - (1, 0, 3), (2, 0, 3), (1, 1, 3), (2, 1, 3)])]) + [ + (references.Interval, [(1, 0), (1, 2)]), + (references.Triangle, [(1, 0), (3, 0), (1, 1)]), + (references.Tetrahedron, [(1, 0, 1), (2, 0, 1), (1, 1, 1), (1, 0, 3)]), + (references.Quadrilateral, [(1, 0), (3, 0), (1, 1), (3, 1)]), + ( + references.Hexahedron, + [ + (1, 0, 1), + (2, 0, 1), + (1, 1, 1), + (2, 1, 1), + (1, 0, 3), + (2, 0, 3), + (1, 1, 3), + (2, 1, 3), + ], + ), + ], +) def test_jacobian(ReferenceClass, points): ref = ReferenceClass(vertices=points) assert ref.jacobian() == 2 diff --git a/test/test_stiffness_matrix.py b/test/test_stiffness_matrix.py index d109af9c..9d85e0b9 100644 --- a/test/test_stiffness_matrix.py +++ b/test/test_stiffness_matrix.py @@ -26,7 +26,7 @@ def test_stiffness_matrix(): [1, -half, -half, 0], [-half, 1, 0, -half], [-half, 0, 1, -half], - [0, -half, -half, 1] + [0, -half, -half, 1], ] for row1, row2 in zip(matrix, actual_matrix): diff --git a/test/test_tensor_product.py b/test/test_tensor_product.py index 7a59ec69..85a3e9a8 100644 --- a/test/test_tensor_product.py +++ b/test/test_tensor_product.py @@ -14,34 +14,56 @@ def make_lattice(cell, N=3): if cell == "interval": return [[sympy.Rational(i, N)] for i in range(N + 1)] if cell == "triangle": - return [[sympy.Rational(i, N), sympy.Rational(j, N)] - for i in range(N + 1) for j in range(N + 1 - i)] + return [ + [sympy.Rational(i, N), sympy.Rational(j, N)] + for i in range(N + 1) + for j in range(N + 1 - i) + ] if cell == "tetrahedron": - return [[sympy.Rational(i, N), sympy.Rational(j, N), sympy.Rational(k, N)] - for i in range(N + 1) for j in range(N + 1 - i) for k in range(N + 1 - i - j)] + return [ + [sympy.Rational(i, N), sympy.Rational(j, N), sympy.Rational(k, N)] + for i in range(N + 1) + for j in range(N + 1 - i) + for k in range(N + 1 - i - j) + ] if cell == "quadrilateral": - return [[sympy.Rational(i, N), sympy.Rational(j, N)] - for i in range(N + 1) for j in range(N + 1)] + return [ + [sympy.Rational(i, N), sympy.Rational(j, N)] for i in range(N + 1) for j in range(N + 1) + ] if cell == "hexahedron": - return [[sympy.Rational(i, N), sympy.Rational(j, N), sympy.Rational(k, N)] - for i in range(N + 1) for j in range(N + 1) for k in range(N + 1)] + return [ + [sympy.Rational(i, N), sympy.Rational(j, N), sympy.Rational(k, N)] + for i in range(N + 1) + for j in range(N + 1) + for k in range(N + 1) + ] if cell == "prism": - return [[sympy.Rational(i, N), sympy.Rational(j, N), sympy.Rational(k, N)] - for i in range(N + 1) for j in range(N + 1 - i) for k in range(N + 1)] + return [ + [sympy.Rational(i, N), sympy.Rational(j, N), sympy.Rational(k, N)] + for i in range(N + 1) + for j in range(N + 1 - i) + for k in range(N + 1) + ] if cell == "pyramid": - return [[sympy.Rational(i, N), sympy.Rational(j, N), sympy.Rational(k, N)] - for i in range(N + 1) for j in range(N + 1) for k in range(N + 1 - max(i, j))] + return [ + [sympy.Rational(i, N), sympy.Rational(j, N), sympy.Rational(k, N)] + for i in range(N + 1) + for j in range(N + 1) + for k in range(N + 1 - max(i, j)) + ] @pytest.mark.parametrize( ("cell_type", "element_type", "order", "kwargs"), - [[reference, element, order, kwargs] - for reference, i in test_elements.items() for element, j in i.items() - for kwargs, k in j for order in k]) -def test_element( - elements_to_test, cells_to_test, cell_type, element_type, order, kwargs, - speed -): + [ + [reference, element, order, kwargs] + for reference, i in test_elements.items() + for element, j in i.items() + for kwargs, k in j + for order in k + ], +) +def test_element(elements_to_test, cells_to_test, cell_type, element_type, order, kwargs, speed): """Run tests for each element.""" if elements_to_test != "ALL" and element_type not in elements_to_test: pytest.skip() diff --git a/test/utils.py b/test/utils.py index 90d58ece..d303db3b 100644 --- a/test/utils.py +++ b/test/utils.py @@ -2,20 +2,27 @@ import typing -test_elements: typing.Dict[str, typing.Dict[ - str, - typing.List[typing.Tuple[typing.Dict[str, typing.Any], typing.Iterable]] -]] = { +test_elements: typing.Dict[ + str, typing.Dict[str, typing.List[typing.Tuple[typing.Dict[str, typing.Any], typing.Iterable]]] +] = { "interval": { - "P": [({"variant": "equispaced"}, range(6)), ({"variant": "lobatto"}, range(4)), - ({"variant": "legendre"}, range(5))], - "vP": [({"variant": "equispaced"}, range(6)), ({"variant": "lobatto"}, range(3)), - ({"variant": "radau"}, range(3)), ({"variant": "legendre"}, range(3))], + "P": [ + ({"variant": "equispaced"}, range(6)), + ({"variant": "lobatto"}, range(4)), + ({"variant": "legendre"}, range(5)), + ], + "vP": [ + ({"variant": "equispaced"}, range(6)), + ({"variant": "lobatto"}, range(3)), + ({"variant": "radau"}, range(3)), + ({"variant": "legendre"}, range(3)), + ], "dPc": [({"variant": "equispaced"}, range(6)), ({"variant": "lobatto"}, range(4))], - "bubble": [({"variant": "equispaced"}, range(2, 6)), - ({"variant": "lobatto"}, range(2, 3))], - "serendipity": [({"variant": "equispaced"}, range(1, 6)), - ({"variant": "lobatto"}, range(1, 3))], + "bubble": [({"variant": "equispaced"}, range(2, 6)), ({"variant": "lobatto"}, range(2, 3))], + "serendipity": [ + ({"variant": "equispaced"}, range(1, 6)), + ({"variant": "lobatto"}, range(1, 3)), + ], "Hermite": [({}, [3])], "Bernstein": [({}, range(1, 4))], "Taylor": [({}, range(0, 5))], @@ -28,12 +35,10 @@ "P": [({"variant": "equispaced"}, range(5))], "vP": [({"variant": "equispaced"}, range(5))], "matrix Lagrange": [({"variant": "equispaced"}, range(3))], - "symmetric matrix Lagrange": [ - ({"variant": "equispaced"}, range(3))], + "symmetric matrix Lagrange": [({"variant": "equispaced"}, range(3))], "bubble": [({"variant": "equispaced"}, range(3, 5))], "bubble enriched Lagrange": [({"variant": "equispaced"}, range(1, 3))], - "bubble enriched vector Lagrange": - [({"variant": "equispaced"}, range(1, 3))], + "bubble enriched vector Lagrange": [({"variant": "equispaced"}, range(1, 3))], "CR": [({"variant": "equispaced"}, [1, 3, 5])], "conforming CR": [({}, range(1, 6))], "HHJ": [({"variant": "equispaced"}, range(3))], @@ -60,10 +65,12 @@ "Bernardi-Raugel": [({}, [1])], "Guzman-Neilan": [({}, [1])], "Wu-Xu": [({}, [3])], - "transition": [({"edge_orders": [1, 1, 1], "variant": "equispaced"}, range(1, 6)), - ({"edge_orders": [2, 2, 1], "variant": "equispaced"}, range(1, 6)), - ({"edge_orders": [3, 3, 1], "variant": "equispaced"}, range(1, 6)), - ({"edge_orders": [3, 3, 2], "variant": "equispaced"}, range(1, 6))], + "transition": [ + ({"edge_orders": [1, 1, 1], "variant": "equispaced"}, range(1, 6)), + ({"edge_orders": [2, 2, 1], "variant": "equispaced"}, range(1, 6)), + ({"edge_orders": [3, 3, 1], "variant": "equispaced"}, range(1, 6)), + ({"edge_orders": [3, 3, 2], "variant": "equispaced"}, range(1, 6)), + ], "P1-iso-P2": [({}, [1])], "EG": [({}, range(1, 4))], "LFEG": [({}, range(1, 4))], @@ -93,22 +100,72 @@ "Bernardi-Raugel": [({}, [1, 2])], "Guzman-Neilan": [({}, [1, 2])], "Wu-Xu": [({}, [4])], - "transition": [({"edge_orders": [1, 1, 1, 1, 1, 1], "face_orders": [1, 1, 1, 1], - "variant": "equispaced"}, range(1, 5)), - ({"edge_orders": [1, 1, 1, 1, 1, 1], "face_orders": [3, 3, 1, 1], - "variant": "equispaced"}, range(1, 5)), - ({"edge_orders": [1, 1, 1, 1, 1, 1], "face_orders": [3, 3, 4, 2], - "variant": "equispaced"}, range(1, 5)), - ({"edge_orders": [1, 1, 1, 1, 1, 1], "face_orders": [4, 4, 1, 1], - "variant": "equispaced"}, range(1, 5)), - ({"edge_orders": [2, 2, 2, 2, 2, 2], "face_orders": [1, 1, 1, 1], - "variant": "equispaced"}, range(1, 5)), - ({"edge_orders": [3, 2, 1, 2, 1, 1], "face_orders": [1, 1, 1, 1], - "variant": "equispaced"}, range(1, 5)), - ({"edge_orders": [1, 1, 3, 1, 3, 1], "face_orders": [1, 1, 1, 1], - "variant": "equispaced"}, range(1, 5)), - ({"edge_orders": [1, 1, 2, 1, 2, 5], "face_orders": [3, 3, 2, 5], - "variant": "equispaced"}, range(1, 5))], + "transition": [ + ( + { + "edge_orders": [1, 1, 1, 1, 1, 1], + "face_orders": [1, 1, 1, 1], + "variant": "equispaced", + }, + range(1, 5), + ), + ( + { + "edge_orders": [1, 1, 1, 1, 1, 1], + "face_orders": [3, 3, 1, 1], + "variant": "equispaced", + }, + range(1, 5), + ), + ( + { + "edge_orders": [1, 1, 1, 1, 1, 1], + "face_orders": [3, 3, 4, 2], + "variant": "equispaced", + }, + range(1, 5), + ), + ( + { + "edge_orders": [1, 1, 1, 1, 1, 1], + "face_orders": [4, 4, 1, 1], + "variant": "equispaced", + }, + range(1, 5), + ), + ( + { + "edge_orders": [2, 2, 2, 2, 2, 2], + "face_orders": [1, 1, 1, 1], + "variant": "equispaced", + }, + range(1, 5), + ), + ( + { + "edge_orders": [3, 2, 1, 2, 1, 1], + "face_orders": [1, 1, 1, 1], + "variant": "equispaced", + }, + range(1, 5), + ), + ( + { + "edge_orders": [1, 1, 3, 1, 3, 1], + "face_orders": [1, 1, 1, 1], + "variant": "equispaced", + }, + range(1, 5), + ), + ( + { + "edge_orders": [1, 1, 2, 1, 2, 5], + "face_orders": [3, 3, 2, 5], + "variant": "equispaced", + }, + range(1, 5), + ), + ], "EG": [({}, range(1, 3))], "LFEG": [({}, range(1, 3))], }, @@ -118,8 +175,10 @@ "vQ": [({"variant": "equispaced"}, range(4)), ({"variant": "lobatto"}, range(3))], "dPc": [({"variant": "equispaced"}, range(4)), ({"variant": "lobatto"}, range(3))], "vector dPc": [({"variant": "equispaced"}, range(4)), ({"variant": "lobatto"}, range(3))], - "serendipity": [({"variant": "equispaced"}, range(1, 4)), - ({"variant": "lobatto"}, range(1, 3))], + "serendipity": [ + ({"variant": "equispaced"}, range(1, 4)), + ({"variant": "lobatto"}, range(1, 3)), + ], "direct serendipity": [({}, range(1, 7))], "Scurl": [({"variant": "equispaced"}, range(1, 4)), ({"variant": "lobatto"}, range(1, 3))], "Sdiv": [({"variant": "equispaced"}, range(1, 4)), ({"variant": "lobatto"}, range(1, 3))], @@ -147,8 +206,10 @@ "vQ": [({"variant": "equispaced"}, range(3)), ({"variant": "lobatto"}, range(3))], "dPc": [({"variant": "equispaced"}, range(3)), ({"variant": "lobatto"}, range(3))], "vector dPc": [({"variant": "equispaced"}, range(3)), ({"variant": "lobatto"}, range(3))], - "serendipity": [({"variant": "equispaced"}, range(1, 3)), - ({"variant": "lobatto"}, range(1, 3))], + "serendipity": [ + ({"variant": "equispaced"}, range(1, 3)), + ({"variant": "lobatto"}, range(1, 3)), + ], "Scurl": [({"variant": "equispaced"}, range(1, 3)), ({"variant": "lobatto"}, range(1, 3))], "Sdiv": [({"variant": "equispaced"}, range(1, 3)), ({"variant": "lobatto"}, range(1, 3))], "Qcurl": [({"variant": "equispaced"}, range(1, 3))], @@ -171,5 +232,5 @@ }, "pyramid": { "Lagrange": [({"variant": "equispaced"}, range(4))], - } + }, } diff --git a/update_readme.py b/update_readme.py index b4f1fb17..6fe4249f 100644 --- a/update_readme.py +++ b/update_readme.py @@ -4,8 +4,16 @@ import symfem -cells = ["interval", "triangle", "quadrilateral", "tetrahedron", - "hexahedron", "prism", "pyramid", "dual polygon"] +cells = [ + "interval", + "triangle", + "quadrilateral", + "tetrahedron", + "hexahedron", + "prism", + "pyramid", + "dual polygon", +] elementlist: typing.Dict[str, typing.List[str]] = {i: [] for i in cells} for e in symfem.create._elementlist: @@ -22,7 +30,6 @@ pre = f.read().split("# Available cells and elements")[0] with open("README.md", "w") as f: - f.write(pre) f.write("# Available cells and elements\n") for cell in cells: @@ -39,8 +46,10 @@ else: f.write(", and ".join([", ".join(str_v[:-1]), str_v[-1]])) f.write(". Its sub-entities are numbered as follows.\n\n") - f.write(f"![The numbering of a reference {cell}]" - f"(img/{cell.replace(' ', '_')}_numbering.png)\n\n") + f.write( + f"![The numbering of a reference {cell}]" + f"(img/{cell.replace(' ', '_')}_numbering.png)\n\n" + ) f.write("### List of supported elements\n") f.write("\n".join([f"- {i}" for i in elementlist[cell]])) From c0af2284de5110ad52c1a70af82618ad76120012 Mon Sep 17 00:00:00 2001 From: Matthew Scroggs Date: Mon, 10 Jun 2024 12:06:44 +0100 Subject: [PATCH 06/15] symfem. --- .github/workflows/style-checks.yml | 2 -- pyproject.toml | 5 +---- symfem/polynomials/__init__.py | 8 ++++---- symfem/polynomials/dual.py | 2 +- symfem/polynomials/legendre.py | 6 +++--- symfem/polynomials/lobatto.py | 8 ++++---- symfem/polynomials/polysets.py | 4 ++-- 7 files changed, 15 insertions(+), 20 deletions(-) diff --git a/.github/workflows/style-checks.yml b/.github/workflows/style-checks.yml index 9b6a5839..4ef1188f 100644 --- a/.github/workflows/style-checks.yml +++ b/.github/workflows/style-checks.yml @@ -20,8 +20,6 @@ jobs: python3 -m ruff check . python3 -m ruff format --check . name: Run ruff checks - - run: python3 -m flake8 . - name: Run flake8 checks - run: python3 -m mypy . name: Run mypy checks - run: python3 -m isort --check . diff --git a/pyproject.toml b/pyproject.toml index cf40b5e0..e74950e9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ repository = "https://github.com/mscroggs/symfem" documentation = "https://symfem.readthedocs.io/en/latest/" [project.optional-dependencies] -style = ["ruff", "flake8", "mypy", "isort"] +style = ["ruff", "mypy"] docs = ["sphinx", "sphinx-autoapi"] optional = ["CairoSVG>=2.6.0"] test = ["pytest", "symfem[optional]", "numpy"] @@ -38,6 +38,3 @@ convention = "google" [tool.mypy] ignore_missing_imports = true - -[tool.isort] -line_length = 100 diff --git a/symfem/polynomials/__init__.py b/symfem/polynomials/__init__.py index d0aae84e..6189d686 100644 --- a/symfem/polynomials/__init__.py +++ b/symfem/polynomials/__init__.py @@ -1,9 +1,9 @@ """Polynomials.""" -from .dual import l2_dual -from .legendre import orthogonal_basis, orthonormal_basis -from .lobatto import lobatto_basis, lobatto_dual_basis -from .polysets import ( +from symfem.polynomials.dual import l2_dual +from symfem.polynomials.legendre import orthogonal_basis, orthonormal_basis +from symfem.polynomials.lobatto import lobatto_basis, lobatto_dual_basis +from symfem.polynomials.polysets import ( Hcurl_polynomials, Hcurl_quolynomials, Hcurl_serendipity, diff --git a/symfem/polynomials/dual.py b/symfem/polynomials/dual.py index a078612e..aefbd084 100644 --- a/symfem/polynomials/dual.py +++ b/symfem/polynomials/dual.py @@ -4,7 +4,7 @@ import sympy -from ..functions import ScalarFunction +from symfem.functions import ScalarFunction def l2_dual(cell: str, poly: typing.List[ScalarFunction]) -> typing.List[ScalarFunction]: diff --git a/symfem/polynomials/legendre.py b/symfem/polynomials/legendre.py index 4fe03a79..7c9637d4 100644 --- a/symfem/polynomials/legendre.py +++ b/symfem/polynomials/legendre.py @@ -4,8 +4,8 @@ import sympy -from ..functions import ScalarFunction -from ..symbols import AxisVariablesNotSingle, x +from symfem.functions import ScalarFunction +from symfem.symbols import AxisVariablesNotSingle, x def _jrc( @@ -670,7 +670,7 @@ def orthonormal_basis( Returns: A set of orthonormal polynomials """ - from ..create import create_reference + from symfem.create import create_reference poly = orthogonal_basis(cell, order, derivs, variables) ref = create_reference(cell) diff --git a/symfem/polynomials/lobatto.py b/symfem/polynomials/lobatto.py index e6ff5253..65c090d3 100644 --- a/symfem/polynomials/lobatto.py +++ b/symfem/polynomials/lobatto.py @@ -2,10 +2,10 @@ import typing -from ..functions import ScalarFunction -from ..symbols import x -from .dual import l2_dual -from .legendre import orthonormal_basis +from symfem.functions import ScalarFunction +from symfem.symbols import x +from symfem.polynomials.dual import l2_dual +from symfem.polynomials.legendre import orthonormal_basis def lobatto_basis_interval(order: int) -> typing.List[ScalarFunction]: diff --git a/symfem/polynomials/polysets.py b/symfem/polynomials/polysets.py index 0bce50c8..d35659c7 100644 --- a/symfem/polynomials/polysets.py +++ b/symfem/polynomials/polysets.py @@ -3,8 +3,8 @@ import typing from itertools import product -from ..functions import ScalarFunction, VectorFunction -from ..symbols import AxisVariablesNotSingle, x +from symfem.functions import ScalarFunction, VectorFunction +from symfem.symbols import AxisVariablesNotSingle, x def polynomial_set_1d( From 39e6852a653221598c6b608afcaf9b1afa168a47 Mon Sep 17 00:00:00 2001 From: Matthew Scroggs Date: Mon, 10 Jun 2024 12:08:06 +0100 Subject: [PATCH 07/15] symfem. --- symfem/elements/abf.py | 16 ++++++------ symfem/elements/ac.py | 16 ++++++------ symfem/elements/alfeld_sorokina.py | 12 ++++----- symfem/elements/argyris.py | 10 ++++---- symfem/elements/aw.py | 14 +++++------ symfem/elements/bddm.py | 16 ++++++------ symfem/elements/bdfm.py | 18 ++++++------- symfem/elements/bdm.py | 16 ++++++------ symfem/elements/bell.py | 12 ++++----- symfem/elements/bernardi_raugel.py | 16 ++++++------ symfem/elements/bernstein.py | 14 +++++------ symfem/elements/bfs.py | 10 ++++---- symfem/elements/bubble.py | 12 ++++----- .../elements/conforming_crouzeix_raviart.py | 12 ++++----- symfem/elements/crouzeix_raviart.py | 12 ++++----- symfem/elements/direct_serendipity.py | 8 +++--- symfem/elements/dpc.py | 17 ++++++++----- symfem/elements/dual.py | 10 ++++---- symfem/elements/enriched_galerkin.py | 8 +++--- symfem/elements/fortin_soulie.py | 10 ++++---- symfem/elements/guzman_neilan.py | 22 ++++++++-------- symfem/elements/hct.py | 12 ++++----- symfem/elements/hermite.py | 10 ++++---- symfem/elements/hhj.py | 16 ++++++------ symfem/elements/huang_zhang.py | 14 +++++------ symfem/elements/kmv.py | 12 ++++----- symfem/elements/lagrange.py | 17 ++++++++----- symfem/elements/lagrange_prism.py | 21 +++++++++++----- symfem/elements/lagrange_pyramid.py | 12 ++++----- symfem/elements/morley.py | 10 ++++---- symfem/elements/morley_wang_xu.py | 10 ++++---- symfem/elements/mtw.py | 18 ++++++------- symfem/elements/nedelec.py | 16 ++++++------ symfem/elements/nedelec_prism.py | 25 +++++++++++-------- symfem/elements/p1_iso_p2.py | 12 ++++----- symfem/elements/p1_macro.py | 14 +++++------ symfem/elements/q.py | 14 +++++------ symfem/elements/rannacher_turek.py | 10 ++++---- symfem/elements/regge.py | 16 ++++++------ symfem/elements/rhct.py | 12 ++++----- symfem/elements/rt.py | 14 +++++------ symfem/elements/serendipity.py | 14 +++++------ symfem/elements/taylor.py | 14 +++++------ symfem/elements/tnt.py | 16 ++++++------ symfem/elements/transition.py | 16 ++++++------ symfem/elements/trimmed_serendipity.py | 16 ++++++------ symfem/elements/vector_enriched_galerkin.py | 14 +++++------ symfem/elements/wu_xu.py | 10 ++++---- 48 files changed, 345 insertions(+), 321 deletions(-) diff --git a/symfem/elements/abf.py b/symfem/elements/abf.py index 1801ea5b..4adff572 100644 --- a/symfem/elements/abf.py +++ b/symfem/elements/abf.py @@ -6,19 +6,19 @@ import typing -from ..finite_element import CiarletElement -from ..functionals import ( +from symfem.finite_element import CiarletElement +from symfem.functionals import ( IntegralMoment, IntegralOfDivergenceAgainst, ListOfFunctionals, NormalIntegralMoment, ) -from ..functions import FunctionInput -from ..moments import make_integral_moment_dofs -from ..references import NonDefaultReferenceError, Reference -from ..symbols import x -from .lagrange import Lagrange -from .q import Nedelec +from symfem.functions import FunctionInput +from symfem.moments import make_integral_moment_dofs +from symfem.references import NonDefaultReferenceError, Reference +from symfem.symbols import x +from symfem.elements.lagrange import Lagrange +from symfem.elements.q import Nedelec class ArnoldBoffiFalk(CiarletElement): diff --git a/symfem/elements/ac.py b/symfem/elements/ac.py index 2dc408ac..9af50b27 100644 --- a/symfem/elements/ac.py +++ b/symfem/elements/ac.py @@ -6,14 +6,14 @@ import typing -from ..finite_element import CiarletElement -from ..functionals import IntegralAgainst, ListOfFunctionals, NormalIntegralMoment -from ..functions import FunctionInput -from ..moments import make_integral_moment_dofs -from ..polynomials import Hdiv_serendipity, polynomial_set_vector -from ..references import NonDefaultReferenceError, Reference -from ..symbols import x -from .dpc import DPC +from symfem.finite_element import CiarletElement +from symfem.functionals import IntegralAgainst, ListOfFunctionals, NormalIntegralMoment +from symfem.functions import FunctionInput +from symfem.moments import make_integral_moment_dofs +from symfem.polynomials import Hdiv_serendipity, polynomial_set_vector +from symfem.references import NonDefaultReferenceError, Reference +from symfem.symbols import x +from symfem.elements.dpc import DPC class AC(CiarletElement): diff --git a/symfem/elements/alfeld_sorokina.py b/symfem/elements/alfeld_sorokina.py index 607fab5d..47460225 100644 --- a/symfem/elements/alfeld_sorokina.py +++ b/symfem/elements/alfeld_sorokina.py @@ -8,12 +8,12 @@ import sympy -from ..finite_element import CiarletElement -from ..functionals import DotPointEvaluation, ListOfFunctionals, PointDivergenceEvaluation -from ..functions import FunctionInput, VectorFunction -from ..piecewise_functions import PiecewiseFunction -from ..references import NonDefaultReferenceError, Reference -from ..symbols import x +from symfem.finite_element import CiarletElement +from symfem.functionals import DotPointEvaluation, ListOfFunctionals, PointDivergenceEvaluation +from symfem.functions import FunctionInput, VectorFunction +from symfem.piecewise_functions import PiecewiseFunction +from symfem.references import NonDefaultReferenceError, Reference +from symfem.symbols import x class AlfeldSorokina(CiarletElement): diff --git a/symfem/elements/argyris.py b/symfem/elements/argyris.py index f4d6f289..40941bfd 100644 --- a/symfem/elements/argyris.py +++ b/symfem/elements/argyris.py @@ -6,17 +6,17 @@ import typing -from ..finite_element import CiarletElement -from ..functionals import ( +from symfem.finite_element import CiarletElement +from symfem.functionals import ( ListOfFunctionals, PointComponentSecondDerivativeEvaluation, PointDirectionalDerivativeEvaluation, PointEvaluation, PointNormalDerivativeEvaluation, ) -from ..functions import FunctionInput -from ..polynomials import polynomial_set_1d -from ..references import NonDefaultReferenceError, Reference +from symfem.functions import FunctionInput +from symfem.polynomials import polynomial_set_1d +from symfem.references import NonDefaultReferenceError, Reference class Argyris(CiarletElement): diff --git a/symfem/elements/aw.py b/symfem/elements/aw.py index 0297b808..da057fd8 100644 --- a/symfem/elements/aw.py +++ b/symfem/elements/aw.py @@ -9,18 +9,18 @@ import sympy -from ..finite_element import CiarletElement -from ..functionals import ( +from symfem.finite_element import CiarletElement +from symfem.functionals import ( InnerProductIntegralMoment, IntegralMoment, ListOfFunctionals, PointInnerProduct, ) -from ..functions import FunctionInput -from ..polynomials import polynomial_set_vector -from ..references import Reference -from ..symbols import x -from .lagrange import Lagrange +from symfem.functions import FunctionInput +from symfem.polynomials import polynomial_set_vector +from symfem.references import Reference +from symfem.symbols import x +from symfem.elements.lagrange import Lagrange class ArnoldWinther(CiarletElement): diff --git a/symfem/elements/bddm.py b/symfem/elements/bddm.py index 75c9aeca..11b1718a 100644 --- a/symfem/elements/bddm.py +++ b/symfem/elements/bddm.py @@ -6,14 +6,14 @@ import typing -from ..finite_element import CiarletElement -from ..functionals import IntegralMoment, ListOfFunctionals, NormalIntegralMoment -from ..functions import FunctionInput, VectorFunction -from ..moments import make_integral_moment_dofs -from ..polynomials import polynomial_set_vector -from ..references import NonDefaultReferenceError, Reference -from ..symbols import x -from .dpc import DPC, VectorDPC +from symfem.finite_element import CiarletElement +from symfem.functionals import IntegralMoment, ListOfFunctionals, NormalIntegralMoment +from symfem.functions import FunctionInput, VectorFunction +from symfem.moments import make_integral_moment_dofs +from symfem.polynomials import polynomial_set_vector +from symfem.references import NonDefaultReferenceError, Reference +from symfem.symbols import x +from symfem.elements.dpc import DPC, VectorDPC def bddf_polyset(reference: Reference, order: int) -> typing.List[FunctionInput]: diff --git a/symfem/elements/bdfm.py b/symfem/elements/bdfm.py index 21ac8198..bf9430c1 100644 --- a/symfem/elements/bdfm.py +++ b/symfem/elements/bdfm.py @@ -6,15 +6,15 @@ import typing -from ..finite_element import CiarletElement -from ..functionals import IntegralMoment, ListOfFunctionals, NormalIntegralMoment -from ..functions import FunctionInput -from ..moments import make_integral_moment_dofs -from ..polynomials import polynomial_set_vector -from ..references import NonDefaultReferenceError, Reference -from ..symbols import x -from .dpc import DPC, VectorDPC -from .lagrange import Lagrange, VectorLagrange +from symfem.finite_element import CiarletElement +from symfem.functionals import IntegralMoment, ListOfFunctionals, NormalIntegralMoment +from symfem.functions import FunctionInput +from symfem.moments import make_integral_moment_dofs +from symfem.polynomials import polynomial_set_vector +from symfem.references import NonDefaultReferenceError, Reference +from symfem.symbols import x +from symfem.elements.dpc import DPC, VectorDPC +from symfem.elements.lagrange import Lagrange, VectorLagrange def bdfm_polyset(reference: Reference, order: int) -> typing.List[FunctionInput]: diff --git a/symfem/elements/bdm.py b/symfem/elements/bdm.py index b95f1a8a..d4f00d16 100644 --- a/symfem/elements/bdm.py +++ b/symfem/elements/bdm.py @@ -6,14 +6,14 @@ import typing -from ..finite_element import CiarletElement -from ..functionals import IntegralMoment, ListOfFunctionals, NormalIntegralMoment -from ..functions import FunctionInput -from ..moments import make_integral_moment_dofs -from ..polynomials import polynomial_set_vector -from ..references import NonDefaultReferenceError, Reference -from .lagrange import Lagrange -from .nedelec import NedelecFirstKind +from symfem.finite_element import CiarletElement +from symfem.functionals import IntegralMoment, ListOfFunctionals, NormalIntegralMoment +from symfem.functions import FunctionInput +from symfem.moments import make_integral_moment_dofs +from symfem.polynomials import polynomial_set_vector +from symfem.references import NonDefaultReferenceError, Reference +from symfem.elements.lagrange import Lagrange +from symfem.elements.nedelec import NedelecFirstKind class BDM(CiarletElement): diff --git a/symfem/elements/bell.py b/symfem/elements/bell.py index 0ce87ff0..db269feb 100644 --- a/symfem/elements/bell.py +++ b/symfem/elements/bell.py @@ -5,12 +5,12 @@ import typing -from ..finite_element import CiarletElement -from ..functionals import DerivativePointEvaluation, ListOfFunctionals, PointEvaluation -from ..functions import FunctionInput -from ..polynomials import polynomial_set_1d -from ..references import Reference -from ..symbols import x +from symfem.finite_element import CiarletElement +from symfem.functionals import DerivativePointEvaluation, ListOfFunctionals, PointEvaluation +from symfem.functions import FunctionInput +from symfem.polynomials import polynomial_set_1d +from symfem.references import Reference +from symfem.symbols import x class Bell(CiarletElement): diff --git a/symfem/elements/bernardi_raugel.py b/symfem/elements/bernardi_raugel.py index 8b34af36..40eaccc2 100644 --- a/symfem/elements/bernardi_raugel.py +++ b/symfem/elements/bernardi_raugel.py @@ -8,19 +8,19 @@ import sympy -from ..finite_element import CiarletElement -from ..functionals import ( +from symfem.finite_element import CiarletElement +from symfem.functionals import ( DivergenceIntegralMoment, DotPointEvaluation, ListOfFunctionals, NormalIntegralMoment, ) -from ..functions import FunctionInput -from ..moments import make_integral_moment_dofs -from ..polynomials import polynomial_set_vector -from ..references import NonDefaultReferenceError, Reference -from ..symbols import x -from .lagrange import Lagrange +from symfem.functions import FunctionInput +from symfem.moments import make_integral_moment_dofs +from symfem.polynomials import polynomial_set_vector +from symfem.references import NonDefaultReferenceError, Reference +from symfem.symbols import x +from symfem.elements.lagrange import Lagrange class BernardiRaugel(CiarletElement): diff --git a/symfem/elements/bernstein.py b/symfem/elements/bernstein.py index 0d981e9f..175a6be4 100644 --- a/symfem/elements/bernstein.py +++ b/symfem/elements/bernstein.py @@ -9,13 +9,13 @@ import sympy -from ..finite_element import CiarletElement -from ..functionals import BaseFunctional, ListOfFunctionals, PointEvaluation -from ..functions import AnyFunction, FunctionInput -from ..geometry import PointType -from ..polynomials import orthogonal_basis, polynomial_set_1d -from ..references import Reference -from ..symbols import AxisVariablesNotSingle, t, x +from symfem.finite_element import CiarletElement +from symfem.functionals import BaseFunctional, ListOfFunctionals, PointEvaluation +from symfem.functions import AnyFunction, FunctionInput +from symfem.geometry import PointType +from symfem.polynomials import orthogonal_basis, polynomial_set_1d +from symfem.references import Reference +from symfem.symbols import AxisVariablesNotSingle, t, x def single_choose(n: int, k: int) -> sympy.core.expr.Expr: diff --git a/symfem/elements/bfs.py b/symfem/elements/bfs.py index 1768790a..3ce01f39 100644 --- a/symfem/elements/bfs.py +++ b/symfem/elements/bfs.py @@ -6,11 +6,11 @@ import typing -from ..finite_element import CiarletElement -from ..functionals import DerivativePointEvaluation, ListOfFunctionals, PointEvaluation -from ..functions import FunctionInput -from ..polynomials import quolynomial_set_1d -from ..references import Reference +from symfem.finite_element import CiarletElement +from symfem.functionals import DerivativePointEvaluation, ListOfFunctionals, PointEvaluation +from symfem.functions import FunctionInput +from symfem.polynomials import quolynomial_set_1d +from symfem.references import Reference class BognerFoxSchmit(CiarletElement): diff --git a/symfem/elements/bubble.py b/symfem/elements/bubble.py index 178cee72..aa7247da 100644 --- a/symfem/elements/bubble.py +++ b/symfem/elements/bubble.py @@ -9,11 +9,11 @@ import sympy -from ..finite_element import CiarletElement -from ..functionals import DotPointEvaluation, ListOfFunctionals, PointEvaluation -from ..functions import FunctionInput -from ..references import Reference -from .lagrange import Lagrange +from symfem.finite_element import CiarletElement +from symfem.functionals import DotPointEvaluation, ListOfFunctionals, PointEvaluation +from symfem.functions import FunctionInput +from symfem.references import Reference +from symfem.elements.lagrange import Lagrange class Bubble(CiarletElement): @@ -27,7 +27,7 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" order: The polynomial order variant: The variant of the element """ - from .. import create_element + from symfem import create_element p1 = create_element(reference.name, "Lagrange", 1, vertices=reference.vertices) bubble = 1 diff --git a/symfem/elements/conforming_crouzeix_raviart.py b/symfem/elements/conforming_crouzeix_raviart.py index 27369661..20bd19c5 100644 --- a/symfem/elements/conforming_crouzeix_raviart.py +++ b/symfem/elements/conforming_crouzeix_raviart.py @@ -8,12 +8,12 @@ import sympy -from ..finite_element import CiarletElement -from ..functionals import ListOfFunctionals, PointEvaluation -from ..functions import FunctionInput -from ..polynomials import polynomial_set_1d -from ..references import NonDefaultReferenceError, Reference -from ..symbols import x +from symfem.finite_element import CiarletElement +from symfem.functionals import ListOfFunctionals, PointEvaluation +from symfem.functions import FunctionInput +from symfem.polynomials import polynomial_set_1d +from symfem.references import NonDefaultReferenceError, Reference +from symfem.symbols import x class ConformingCrouzeixRaviart(CiarletElement): diff --git a/symfem/elements/crouzeix_raviart.py b/symfem/elements/crouzeix_raviart.py index bd71fb9a..994474a4 100644 --- a/symfem/elements/crouzeix_raviart.py +++ b/symfem/elements/crouzeix_raviart.py @@ -7,12 +7,12 @@ import typing from itertools import product -from ..finite_element import CiarletElement -from ..functionals import ListOfFunctionals, PointEvaluation -from ..functions import FunctionInput -from ..polynomials import polynomial_set_1d -from ..quadrature import get_quadrature -from ..references import Reference +from symfem.finite_element import CiarletElement +from symfem.functionals import ListOfFunctionals, PointEvaluation +from symfem.functions import FunctionInput +from symfem.polynomials import polynomial_set_1d +from symfem.quadrature import get_quadrature +from symfem.references import Reference class CrouzeixRaviart(CiarletElement): diff --git a/symfem/elements/direct_serendipity.py b/symfem/elements/direct_serendipity.py index cca2b364..7e267e74 100644 --- a/symfem/elements/direct_serendipity.py +++ b/symfem/elements/direct_serendipity.py @@ -4,10 +4,10 @@ (Arbogast, Tao, 2018) """ -from ..finite_element import DirectElement -from ..references import Reference -from ..symbols import x -from .dpc import DPC +from symfem.finite_element import DirectElement +from symfem.references import Reference +from symfem.symbols import x +from symfem.elements.dpc import DPC class DirectSerendipity(DirectElement): diff --git a/symfem/elements/dpc.py b/symfem/elements/dpc.py index 6ecd45a0..6385647b 100644 --- a/symfem/elements/dpc.py +++ b/symfem/elements/dpc.py @@ -5,12 +5,17 @@ import sympy -from ..finite_element import CiarletElement -from ..functionals import DotPointEvaluation, IntegralAgainst, ListOfFunctionals, PointEvaluation -from ..functions import FunctionInput -from ..polynomials import polynomial_set_1d, polynomial_set_vector -from ..references import NonDefaultReferenceError, Reference -from .lagrange import Lagrange +from symfem.finite_element import CiarletElement +from symfem.functionals import ( + DotPointEvaluation, + IntegralAgainst, + ListOfFunctionals, + PointEvaluation, +) +from symfem.functions import FunctionInput +from symfem.polynomials import polynomial_set_1d, polynomial_set_vector +from symfem.references import NonDefaultReferenceError, Reference +from symfem.elements.lagrange import Lagrange class DPC(CiarletElement): diff --git a/symfem/elements/dual.py b/symfem/elements/dual.py index 5e677dc2..4bfc1521 100644 --- a/symfem/elements/dual.py +++ b/symfem/elements/dual.py @@ -8,11 +8,11 @@ import sympy -from ..finite_element import FiniteElement -from ..functions import AnyFunction, FunctionInput, VectorFunction -from ..geometry import PointType, SetOfPoints, SetOfPointsInput -from ..piecewise_functions import PiecewiseFunction -from ..references import DualPolygon, NonDefaultReferenceError +from symfem.finite_element import FiniteElement +from symfem.functions import AnyFunction, FunctionInput, VectorFunction +from symfem.geometry import PointType, SetOfPoints, SetOfPointsInput +from symfem.piecewise_functions import PiecewiseFunction +from symfem.references import DualPolygon, NonDefaultReferenceError class DualCiarletElement(FiniteElement): diff --git a/symfem/elements/enriched_galerkin.py b/symfem/elements/enriched_galerkin.py index 0ea7001b..76bf2ec1 100644 --- a/symfem/elements/enriched_galerkin.py +++ b/symfem/elements/enriched_galerkin.py @@ -4,10 +4,10 @@ (Sun, Liu, 2009). """ -from ..finite_element import EnrichedElement -from ..references import Reference -from .lagrange import Lagrange -from .q import Q +from symfem.finite_element import EnrichedElement +from symfem.references import Reference +from symfem.elements.lagrange import Lagrange +from symfem.elements.q import Q class EnrichedGalerkin(EnrichedElement): diff --git a/symfem/elements/fortin_soulie.py b/symfem/elements/fortin_soulie.py index 59fc384e..f2da2b03 100644 --- a/symfem/elements/fortin_soulie.py +++ b/symfem/elements/fortin_soulie.py @@ -8,11 +8,11 @@ import sympy -from ..finite_element import CiarletElement -from ..functionals import ListOfFunctionals, PointEvaluation -from ..functions import FunctionInput -from ..polynomials import polynomial_set_1d -from ..references import NonDefaultReferenceError, Reference +from symfem.finite_element import CiarletElement +from symfem.functionals import ListOfFunctionals, PointEvaluation +from symfem.functions import FunctionInput +from symfem.polynomials import polynomial_set_1d +from symfem.references import NonDefaultReferenceError, Reference class FortinSoulie(CiarletElement): diff --git a/symfem/elements/guzman_neilan.py b/symfem/elements/guzman_neilan.py index cab8f9f6..4bc710a5 100644 --- a/symfem/elements/guzman_neilan.py +++ b/symfem/elements/guzman_neilan.py @@ -8,15 +8,15 @@ import sympy -from ..finite_element import CiarletElement -from ..functionals import DotPointEvaluation, ListOfFunctionals, NormalIntegralMoment -from ..functions import FunctionInput, VectorFunction -from ..geometry import SetOfPoints, SetOfPointsInput -from ..moments import make_integral_moment_dofs -from ..piecewise_functions import PiecewiseFunction -from ..references import NonDefaultReferenceError, Reference -from .bernardi_raugel import BernardiRaugel -from .lagrange import Lagrange, VectorLagrange +from symfem.finite_element import CiarletElement +from symfem.functionals import DotPointEvaluation, ListOfFunctionals, NormalIntegralMoment +from symfem.functions import FunctionInput, VectorFunction +from symfem.geometry import SetOfPoints, SetOfPointsInput +from symfem.moments import make_integral_moment_dofs +from symfem.piecewise_functions import PiecewiseFunction +from symfem.references import NonDefaultReferenceError, Reference +from symfem.elements.bernardi_raugel import BernardiRaugel +from symfem.elements.lagrange import Lagrange, VectorLagrange class GuzmanNeilan(CiarletElement): @@ -121,7 +121,7 @@ def _make_polyset_triangle( """ assert order == 1 - from ._guzman_neilan_triangle import coeffs + from symfem.elements._guzman_neilan_triangle import coeffs mid = reference.midpoint() @@ -165,7 +165,7 @@ def _make_polyset_tetrahedron( The polynomial set """ assert order in [1, 2] - from ._guzman_neilan_tetrahedron import coeffs + from symfem.elements._guzman_neilan_tetrahedron import coeffs mid = reference.midpoint() diff --git a/symfem/elements/hct.py b/symfem/elements/hct.py index 9fc8f742..eb37d850 100644 --- a/symfem/elements/hct.py +++ b/symfem/elements/hct.py @@ -8,17 +8,17 @@ import sympy -from ..finite_element import CiarletElement -from ..functionals import ( +from symfem.finite_element import CiarletElement +from symfem.functionals import ( DerivativePointEvaluation, ListOfFunctionals, PointEvaluation, PointNormalDerivativeEvaluation, ) -from ..functions import FunctionInput, ScalarFunction -from ..piecewise_functions import PiecewiseFunction -from ..references import NonDefaultReferenceError, Reference -from ..symbols import x +from symfem.functions import FunctionInput, ScalarFunction +from symfem.piecewise_functions import PiecewiseFunction +from symfem.references import NonDefaultReferenceError, Reference +from symfem.symbols import x class HsiehCloughTocher(CiarletElement): diff --git a/symfem/elements/hermite.py b/symfem/elements/hermite.py index 5485bb9b..d6cda30a 100644 --- a/symfem/elements/hermite.py +++ b/symfem/elements/hermite.py @@ -6,11 +6,11 @@ import typing -from ..finite_element import CiarletElement -from ..functionals import DerivativePointEvaluation, ListOfFunctionals, PointEvaluation -from ..functions import FunctionInput -from ..polynomials import polynomial_set_1d -from ..references import Reference +from symfem.finite_element import CiarletElement +from symfem.functionals import DerivativePointEvaluation, ListOfFunctionals, PointEvaluation +from symfem.functions import FunctionInput +from symfem.polynomials import polynomial_set_1d +from symfem.references import Reference class Hermite(CiarletElement): diff --git a/symfem/elements/hhj.py b/symfem/elements/hhj.py index fdcb15cf..1e9ea5f4 100644 --- a/symfem/elements/hhj.py +++ b/symfem/elements/hhj.py @@ -13,14 +13,14 @@ import typing -from ..finite_element import CiarletElement -from ..functionals import IntegralMoment, ListOfFunctionals, NormalInnerProductIntegralMoment -from ..functions import FunctionInput -from ..functions import MatrixFunction as _MatrixFunction -from ..moments import make_integral_moment_dofs -from ..polynomials import polynomial_set_vector -from ..references import NonDefaultReferenceError, Reference -from .lagrange import Lagrange +from symfem.finite_element import CiarletElement +from symfem.functionals import IntegralMoment, ListOfFunctionals, NormalInnerProductIntegralMoment +from symfem.functions import FunctionInput +from symfem.functions import MatrixFunction as _MatrixFunction +from symfem.moments import make_integral_moment_dofs +from symfem.polynomials import polynomial_set_vector +from symfem.references import NonDefaultReferenceError, Reference +from symfem.elements.lagrange import Lagrange class HellanHerrmannJohnson(CiarletElement): diff --git a/symfem/elements/huang_zhang.py b/symfem/elements/huang_zhang.py index d6465f86..455c2241 100644 --- a/symfem/elements/huang_zhang.py +++ b/symfem/elements/huang_zhang.py @@ -6,18 +6,18 @@ import typing -from ..finite_element import CiarletElement -from ..functionals import ( +from symfem.finite_element import CiarletElement +from symfem.functionals import ( IntegralAgainst, ListOfFunctionals, NormalIntegralMoment, TangentIntegralMoment, ) -from ..functions import FunctionInput, VectorFunction -from ..moments import make_integral_moment_dofs -from ..references import NonDefaultReferenceError, Reference -from ..symbols import x -from .lagrange import Lagrange +from symfem.functions import FunctionInput, VectorFunction +from symfem.moments import make_integral_moment_dofs +from symfem.references import NonDefaultReferenceError, Reference +from symfem.symbols import x +from symfem.elements.lagrange import Lagrange class HuangZhang(CiarletElement): diff --git a/symfem/elements/kmv.py b/symfem/elements/kmv.py index ec6995d9..51d8f961 100644 --- a/symfem/elements/kmv.py +++ b/symfem/elements/kmv.py @@ -8,12 +8,12 @@ import sympy -from ..finite_element import CiarletElement -from ..functionals import ListOfFunctionals, WeightedPointEvaluation -from ..functions import FunctionInput -from ..polynomials import polynomial_set_1d -from ..references import NonDefaultReferenceError, Reference -from ..symbols import x +from symfem.finite_element import CiarletElement +from symfem.functionals import ListOfFunctionals, WeightedPointEvaluation +from symfem.functions import FunctionInput +from symfem.polynomials import polynomial_set_1d +from symfem.references import NonDefaultReferenceError, Reference +from symfem.symbols import x def kmv_tri_polyset(m: int, mf: int) -> typing.List[FunctionInput]: diff --git a/symfem/elements/lagrange.py b/symfem/elements/lagrange.py index 28dca27d..c755aeb3 100644 --- a/symfem/elements/lagrange.py +++ b/symfem/elements/lagrange.py @@ -5,17 +5,22 @@ import sympy -from ..finite_element import CiarletElement -from ..functionals import DotPointEvaluation, IntegralAgainst, ListOfFunctionals, PointEvaluation -from ..functions import FunctionInput -from ..polynomials import ( +from symfem.finite_element import CiarletElement +from symfem.functionals import ( + DotPointEvaluation, + IntegralAgainst, + ListOfFunctionals, + PointEvaluation, +) +from symfem.functions import FunctionInput +from symfem.polynomials import ( lobatto_dual_basis, orthonormal_basis, polynomial_set_1d, polynomial_set_vector, ) -from ..quadrature import get_quadrature -from ..references import Reference +from symfem.quadrature import get_quadrature +from symfem.references import Reference class Lagrange(CiarletElement): diff --git a/symfem/elements/lagrange_prism.py b/symfem/elements/lagrange_prism.py index 8c7a1452..24338c54 100644 --- a/symfem/elements/lagrange_prism.py +++ b/symfem/elements/lagrange_prism.py @@ -5,12 +5,21 @@ import sympy -from ..finite_element import CiarletElement -from ..functionals import DotPointEvaluation, IntegralAgainst, ListOfFunctionals, PointEvaluation -from ..functions import FunctionInput -from ..polynomials import orthonormal_basis, prism_polynomial_set_1d, prism_polynomial_set_vector -from ..quadrature import get_quadrature -from ..references import NonDefaultReferenceError, Reference +from symfem.finite_element import CiarletElement +from symfem.functionals import ( + DotPointEvaluation, + IntegralAgainst, + ListOfFunctionals, + PointEvaluation, +) +from symfem.functions import FunctionInput +from symfem.polynomials import ( + orthonormal_basis, + prism_polynomial_set_1d, + prism_polynomial_set_vector, +) +from symfem.quadrature import get_quadrature +from symfem.references import NonDefaultReferenceError, Reference class Lagrange(CiarletElement): diff --git a/symfem/elements/lagrange_pyramid.py b/symfem/elements/lagrange_pyramid.py index d200be9a..d5ab8c24 100644 --- a/symfem/elements/lagrange_pyramid.py +++ b/symfem/elements/lagrange_pyramid.py @@ -9,12 +9,12 @@ import sympy -from ..finite_element import CiarletElement -from ..functionals import IntegralAgainst, ListOfFunctionals, PointEvaluation -from ..functions import FunctionInput -from ..polynomials import orthonormal_basis, pyramid_polynomial_set_1d -from ..quadrature import get_quadrature -from ..references import NonDefaultReferenceError, Reference +from symfem.finite_element import CiarletElement +from symfem.functionals import IntegralAgainst, ListOfFunctionals, PointEvaluation +from symfem.functions import FunctionInput +from symfem.polynomials import orthonormal_basis, pyramid_polynomial_set_1d +from symfem.quadrature import get_quadrature +from symfem.references import NonDefaultReferenceError, Reference class Lagrange(CiarletElement): diff --git a/symfem/elements/morley.py b/symfem/elements/morley.py index 784099ed..09515d56 100644 --- a/symfem/elements/morley.py +++ b/symfem/elements/morley.py @@ -6,11 +6,11 @@ import typing -from ..finite_element import CiarletElement -from ..functionals import ListOfFunctionals, PointEvaluation, PointNormalDerivativeEvaluation -from ..functions import FunctionInput -from ..polynomials import polynomial_set_1d -from ..references import NonDefaultReferenceError, Reference +from symfem.finite_element import CiarletElement +from symfem.functionals import ListOfFunctionals, PointEvaluation, PointNormalDerivativeEvaluation +from symfem.functions import FunctionInput +from symfem.polynomials import polynomial_set_1d +from symfem.references import NonDefaultReferenceError, Reference class Morley(CiarletElement): diff --git a/symfem/elements/morley_wang_xu.py b/symfem/elements/morley_wang_xu.py index da972315..521f2b46 100644 --- a/symfem/elements/morley_wang_xu.py +++ b/symfem/elements/morley_wang_xu.py @@ -6,16 +6,16 @@ import typing -from ..finite_element import CiarletElement -from ..functionals import ( +from symfem.finite_element import CiarletElement +from symfem.functionals import ( IntegralAgainst, IntegralOfDirectionalMultiderivative, ListOfFunctionals, PointEvaluation, ) -from ..functions import FunctionInput -from ..polynomials import polynomial_set_1d -from ..references import NonDefaultReferenceError, Reference +from symfem.functions import FunctionInput +from symfem.polynomials import polynomial_set_1d +from symfem.references import NonDefaultReferenceError, Reference class MorleyWangXu(CiarletElement): diff --git a/symfem/elements/mtw.py b/symfem/elements/mtw.py index 3ef807dc..1445235b 100644 --- a/symfem/elements/mtw.py +++ b/symfem/elements/mtw.py @@ -7,20 +7,20 @@ import typing -from ..finite_element import CiarletElement -from ..functionals import ( +from symfem.finite_element import CiarletElement +from symfem.functionals import ( IntegralMoment, ListOfFunctionals, NormalIntegralMoment, TangentIntegralMoment, ) -from ..functions import FunctionInput, VectorFunction -from ..moments import make_integral_moment_dofs -from ..polynomials import polynomial_set_vector -from ..references import NonDefaultReferenceError, Reference -from ..symbols import x -from .lagrange import Lagrange -from .nedelec import NedelecFirstKind +from symfem.functions import FunctionInput, VectorFunction +from symfem.moments import make_integral_moment_dofs +from symfem.polynomials import polynomial_set_vector +from symfem.references import NonDefaultReferenceError, Reference +from symfem.symbols import x +from symfem.elements.lagrange import Lagrange +from symfem.elements.nedelec import NedelecFirstKind class MardalTaiWinther(CiarletElement): diff --git a/symfem/elements/nedelec.py b/symfem/elements/nedelec.py index d328c3b1..bc5187f4 100644 --- a/symfem/elements/nedelec.py +++ b/symfem/elements/nedelec.py @@ -6,14 +6,14 @@ import typing -from ..finite_element import CiarletElement -from ..functionals import IntegralMoment, ListOfFunctionals, TangentIntegralMoment -from ..functions import FunctionInput -from ..moments import make_integral_moment_dofs -from ..polynomials import Hcurl_polynomials, polynomial_set_vector -from ..references import Reference -from .lagrange import Lagrange, VectorLagrange -from .rt import RaviartThomas +from symfem.finite_element import CiarletElement +from symfem.functionals import IntegralMoment, ListOfFunctionals, TangentIntegralMoment +from symfem.functions import FunctionInput +from symfem.moments import make_integral_moment_dofs +from symfem.polynomials import Hcurl_polynomials, polynomial_set_vector +from symfem.references import Reference +from symfem.elements.lagrange import Lagrange, VectorLagrange +from symfem.elements.rt import RaviartThomas class NedelecFirstKind(CiarletElement): diff --git a/symfem/elements/nedelec_prism.py b/symfem/elements/nedelec_prism.py index b6ee6aa4..22f10022 100644 --- a/symfem/elements/nedelec_prism.py +++ b/symfem/elements/nedelec_prism.py @@ -2,15 +2,20 @@ import typing -from ..finite_element import CiarletElement -from ..functionals import IntegralAgainst, IntegralMoment, ListOfFunctionals, TangentIntegralMoment -from ..functions import FunctionInput -from ..moments import make_integral_moment_dofs -from ..polynomials import Hcurl_polynomials, polynomial_set_1d, polynomial_set_vector -from ..references import NonDefaultReferenceError, Reference -from ..symbols import x -from .lagrange import Lagrange, VectorLagrange -from .q import RaviartThomas as QRT +from symfem.finite_element import CiarletElement +from symfem.functionals import ( + IntegralAgainst, + IntegralMoment, + ListOfFunctionals, + TangentIntegralMoment, +) +from symfem.functions import FunctionInput +from symfem.moments import make_integral_moment_dofs +from symfem.polynomials import Hcurl_polynomials, polynomial_set_1d, polynomial_set_vector +from symfem.references import NonDefaultReferenceError, Reference +from symfem.symbols import x +from symfem.elements.lagrange import Lagrange, VectorLagrange +from symfem.elements.q import RaviartThomas as QRT class Nedelec(CiarletElement): @@ -24,7 +29,7 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced" order: The polynomial order variant: The variant of the element """ - from .. import create_reference + from symfem import create_reference if reference.vertices != reference.reference_vertices: raise NonDefaultReferenceError() diff --git a/symfem/elements/p1_iso_p2.py b/symfem/elements/p1_iso_p2.py index 6ea94ca1..ee43a66e 100644 --- a/symfem/elements/p1_iso_p2.py +++ b/symfem/elements/p1_iso_p2.py @@ -8,12 +8,12 @@ import sympy -from ..finite_element import CiarletElement -from ..functionals import ListOfFunctionals, PointEvaluation -from ..functions import FunctionInput -from ..geometry import SetOfPoints -from ..piecewise_functions import PiecewiseFunction -from ..references import Reference +from symfem.finite_element import CiarletElement +from symfem.functionals import ListOfFunctionals, PointEvaluation +from symfem.functions import FunctionInput +from symfem.geometry import SetOfPoints +from symfem.piecewise_functions import PiecewiseFunction +from symfem.references import Reference class P1IsoP2Interval(CiarletElement): diff --git a/symfem/elements/p1_macro.py b/symfem/elements/p1_macro.py index 951a1519..4999a1a5 100644 --- a/symfem/elements/p1_macro.py +++ b/symfem/elements/p1_macro.py @@ -8,13 +8,13 @@ import sympy -from ..finite_element import CiarletElement -from ..functionals import IntegralAgainst, ListOfFunctionals, PointEvaluation -from ..functions import FunctionInput -from ..geometry import SetOfPoints -from ..piecewise_functions import PiecewiseFunction -from ..references import Reference -from ..symbols import x +from symfem.finite_element import CiarletElement +from symfem.functionals import IntegralAgainst, ListOfFunctionals, PointEvaluation +from symfem.functions import FunctionInput +from symfem.geometry import SetOfPoints +from symfem.piecewise_functions import PiecewiseFunction +from symfem.references import Reference +from symfem.symbols import x class P1Macro(CiarletElement): diff --git a/symfem/elements/q.py b/symfem/elements/q.py index d4f62457..32f6d0f6 100644 --- a/symfem/elements/q.py +++ b/symfem/elements/q.py @@ -5,8 +5,8 @@ import sympy -from ..finite_element import CiarletElement, FiniteElement -from ..functionals import ( +from symfem.finite_element import CiarletElement, FiniteElement +from symfem.functionals import ( DotPointEvaluation, IntegralAgainst, IntegralMoment, @@ -15,9 +15,9 @@ PointEvaluation, TangentIntegralMoment, ) -from ..functions import FunctionInput -from ..moments import make_integral_moment_dofs -from ..polynomials import ( +from symfem.functions import FunctionInput +from symfem.moments import make_integral_moment_dofs +from symfem.polynomials import ( Hcurl_quolynomials, Hdiv_quolynomials, lobatto_dual_basis, @@ -25,8 +25,8 @@ quolynomial_set_1d, quolynomial_set_vector, ) -from ..quadrature import get_quadrature -from ..references import NonDefaultReferenceError, Reference +from symfem.quadrature import get_quadrature +from symfem.references import NonDefaultReferenceError, Reference class Q(CiarletElement): diff --git a/symfem/elements/rannacher_turek.py b/symfem/elements/rannacher_turek.py index 91539df2..697bfcfc 100644 --- a/symfem/elements/rannacher_turek.py +++ b/symfem/elements/rannacher_turek.py @@ -6,11 +6,11 @@ import typing -from ..finite_element import CiarletElement -from ..functionals import ListOfFunctionals, PointEvaluation -from ..functions import FunctionInput -from ..references import NonDefaultReferenceError, Reference -from ..symbols import x +from symfem.finite_element import CiarletElement +from symfem.functionals import ListOfFunctionals, PointEvaluation +from symfem.functions import FunctionInput +from symfem.references import NonDefaultReferenceError, Reference +from symfem.symbols import x class RannacherTurek(CiarletElement): diff --git a/symfem/elements/regge.py b/symfem/elements/regge.py index 86090b2c..69cc3bf6 100644 --- a/symfem/elements/regge.py +++ b/symfem/elements/regge.py @@ -12,20 +12,20 @@ import sympy -from ..finite_element import CiarletElement -from ..functionals import ( +from symfem.finite_element import CiarletElement +from symfem.functionals import ( InnerProductIntegralMoment, IntegralAgainst, IntegralMoment, ListOfFunctionals, PointInnerProduct, ) -from ..functions import FunctionInput -from ..moments import make_integral_moment_dofs -from ..polynomials import polynomial_set_vector -from ..references import Reference -from ..symbols import t, x -from .lagrange import Lagrange +from symfem.functions import FunctionInput +from symfem.moments import make_integral_moment_dofs +from symfem.polynomials import polynomial_set_vector +from symfem.references import Reference +from symfem.symbols import t, x +from symfem.elements.lagrange import Lagrange class Regge(CiarletElement): diff --git a/symfem/elements/rhct.py b/symfem/elements/rhct.py index 8d5bb905..2deb094b 100644 --- a/symfem/elements/rhct.py +++ b/symfem/elements/rhct.py @@ -8,12 +8,12 @@ import sympy -from ..finite_element import CiarletElement -from ..functionals import DerivativePointEvaluation, ListOfFunctionals, PointEvaluation -from ..functions import FunctionInput, ScalarFunction -from ..piecewise_functions import PiecewiseFunction -from ..references import NonDefaultReferenceError, Reference -from ..symbols import x +from symfem.finite_element import CiarletElement +from symfem.functionals import DerivativePointEvaluation, ListOfFunctionals, PointEvaluation +from symfem.functions import FunctionInput, ScalarFunction +from symfem.piecewise_functions import PiecewiseFunction +from symfem.references import NonDefaultReferenceError, Reference +from symfem.symbols import x class ReducedHsiehCloughTocher(CiarletElement): diff --git a/symfem/elements/rt.py b/symfem/elements/rt.py index 9ceddbab..9c93ede1 100644 --- a/symfem/elements/rt.py +++ b/symfem/elements/rt.py @@ -6,13 +6,13 @@ import typing -from ..finite_element import CiarletElement -from ..functionals import IntegralMoment, ListOfFunctionals, NormalIntegralMoment -from ..functions import FunctionInput -from ..moments import make_integral_moment_dofs -from ..polynomials import Hdiv_polynomials, polynomial_set_vector -from ..references import Reference -from .lagrange import Lagrange, VectorLagrange +from symfem.finite_element import CiarletElement +from symfem.functionals import IntegralMoment, ListOfFunctionals, NormalIntegralMoment +from symfem.functions import FunctionInput +from symfem.moments import make_integral_moment_dofs +from symfem.polynomials import Hdiv_polynomials, polynomial_set_vector +from symfem.references import Reference +from symfem.elements.lagrange import Lagrange, VectorLagrange class RaviartThomas(CiarletElement): diff --git a/symfem/elements/serendipity.py b/symfem/elements/serendipity.py index 453b2e54..54580e45 100644 --- a/symfem/elements/serendipity.py +++ b/symfem/elements/serendipity.py @@ -6,25 +6,25 @@ import typing -from ..finite_element import CiarletElement -from ..functionals import ( +from symfem.finite_element import CiarletElement +from symfem.functionals import ( IntegralMoment, ListOfFunctionals, NormalIntegralMoment, PointEvaluation, TangentIntegralMoment, ) -from ..functions import FunctionInput -from ..moments import make_integral_moment_dofs -from ..polynomials import ( +from symfem.functions import FunctionInput +from symfem.moments import make_integral_moment_dofs +from symfem.polynomials import ( Hcurl_serendipity, Hdiv_serendipity, polynomial_set_1d, polynomial_set_vector, serendipity_set_1d, ) -from ..references import NonDefaultReferenceError, Reference -from .dpc import DPC, VectorDPC +from symfem.references import NonDefaultReferenceError, Reference +from symfem.elements.dpc import DPC, VectorDPC class Serendipity(CiarletElement): diff --git a/symfem/elements/taylor.py b/symfem/elements/taylor.py index 45c0e240..e7568c80 100644 --- a/symfem/elements/taylor.py +++ b/symfem/elements/taylor.py @@ -3,13 +3,13 @@ import typing from itertools import product -from ..finite_element import CiarletElement -from ..functionals import DerivativePointEvaluation, IntegralMoment, ListOfFunctionals -from ..functions import FunctionInput -from ..moments import make_integral_moment_dofs -from ..polynomials import polynomial_set_1d -from ..references import Reference -from .lagrange import Lagrange +from symfem.finite_element import CiarletElement +from symfem.functionals import DerivativePointEvaluation, IntegralMoment, ListOfFunctionals +from symfem.functions import FunctionInput +from symfem.moments import make_integral_moment_dofs +from symfem.polynomials import polynomial_set_1d +from symfem.references import Reference +from symfem.elements.lagrange import Lagrange class Taylor(CiarletElement): diff --git a/symfem/elements/tnt.py b/symfem/elements/tnt.py index 99488d76..6837b2f6 100644 --- a/symfem/elements/tnt.py +++ b/symfem/elements/tnt.py @@ -9,8 +9,8 @@ import sympy -from ..finite_element import CiarletElement -from ..functionals import ( +from symfem.finite_element import CiarletElement +from symfem.functionals import ( DerivativeIntegralMoment, IntegralAgainst, ListOfFunctionals, @@ -18,12 +18,12 @@ PointEvaluation, TangentIntegralMoment, ) -from ..functions import FunctionInput, ScalarFunction, VectorFunction -from ..moments import make_integral_moment_dofs -from ..polynomials import orthogonal_basis, quolynomial_set_1d, quolynomial_set_vector -from ..references import NonDefaultReferenceError, Reference -from ..symbols import t, x -from .q import Q +from symfem.functions import FunctionInput, ScalarFunction, VectorFunction +from symfem.moments import make_integral_moment_dofs +from symfem.polynomials import orthogonal_basis, quolynomial_set_1d, quolynomial_set_vector +from symfem.references import NonDefaultReferenceError, Reference +from symfem.symbols import t, x +from symfem.elements.q import Q def p(k: int, v: sympy.core.symbol.Symbol) -> ScalarFunction: diff --git a/symfem/elements/transition.py b/symfem/elements/transition.py index b26c105a..dbf57ba6 100644 --- a/symfem/elements/transition.py +++ b/symfem/elements/transition.py @@ -5,14 +5,14 @@ import sympy -from ..finite_element import CiarletElement -from ..functionals import ListOfFunctionals, PointEvaluation -from ..functions import FunctionInput, ScalarFunction -from ..polynomials import polynomial_set_1d -from ..quadrature import get_quadrature -from ..references import Reference -from ..symbols import x -from .lagrange import Lagrange +from symfem.finite_element import CiarletElement +from symfem.functionals import ListOfFunctionals, PointEvaluation +from symfem.functions import FunctionInput, ScalarFunction +from symfem.polynomials import polynomial_set_1d +from symfem.quadrature import get_quadrature +from symfem.references import Reference +from symfem.symbols import x +from symfem.elements.lagrange import Lagrange class Transition(CiarletElement): diff --git a/symfem/elements/trimmed_serendipity.py b/symfem/elements/trimmed_serendipity.py index 8514d38c..6d682c34 100644 --- a/symfem/elements/trimmed_serendipity.py +++ b/symfem/elements/trimmed_serendipity.py @@ -7,20 +7,20 @@ import typing -from ..finite_element import CiarletElement -from ..functionals import ( +from symfem.finite_element import CiarletElement +from symfem.functionals import ( IntegralAgainst, IntegralMoment, ListOfFunctionals, NormalIntegralMoment, TangentIntegralMoment, ) -from ..functions import FunctionInput, ScalarFunction, VectorFunction -from ..moments import make_integral_moment_dofs -from ..polynomials import polynomial_set_vector -from ..references import NonDefaultReferenceError, Reference -from ..symbols import t, x -from .dpc import DPC, VectorDPC +from symfem.functions import FunctionInput, ScalarFunction, VectorFunction +from symfem.moments import make_integral_moment_dofs +from symfem.polynomials import polynomial_set_vector +from symfem.references import NonDefaultReferenceError, Reference +from symfem.symbols import t, x +from symfem.elements.dpc import DPC, VectorDPC class TrimmedSerendipityHcurl(CiarletElement): diff --git a/symfem/elements/vector_enriched_galerkin.py b/symfem/elements/vector_enriched_galerkin.py index dc228927..10a49ea7 100644 --- a/symfem/elements/vector_enriched_galerkin.py +++ b/symfem/elements/vector_enriched_galerkin.py @@ -6,13 +6,13 @@ import typing -from ..finite_element import CiarletElement, EnrichedElement -from ..functionals import BaseFunctional, IntegralAgainst -from ..functions import FunctionInput, VectorFunction -from ..references import NonDefaultReferenceError, Reference -from ..symbols import x -from .lagrange import VectorLagrange -from .q import VectorQ +from symfem.finite_element import CiarletElement, EnrichedElement +from symfem.functionals import BaseFunctional, IntegralAgainst +from symfem.functions import FunctionInput, VectorFunction +from symfem.references import NonDefaultReferenceError, Reference +from symfem.symbols import x +from symfem.elements.lagrange import VectorLagrange +from symfem.elements.q import VectorQ class Enrichment(CiarletElement): diff --git a/symfem/elements/wu_xu.py b/symfem/elements/wu_xu.py index dc9a212f..38758421 100644 --- a/symfem/elements/wu_xu.py +++ b/symfem/elements/wu_xu.py @@ -6,16 +6,16 @@ import typing -from ..finite_element import CiarletElement -from ..functionals import ( +from symfem.finite_element import CiarletElement +from symfem.functionals import ( DerivativePointEvaluation, IntegralOfDirectionalMultiderivative, ListOfFunctionals, PointEvaluation, ) -from ..functions import FunctionInput -from ..polynomials import polynomial_set_1d -from ..references import NonDefaultReferenceError, Reference +from symfem.functions import FunctionInput +from symfem.polynomials import polynomial_set_1d +from symfem.references import NonDefaultReferenceError, Reference def derivatives(dim: int, order: int) -> typing.List[typing.Tuple[int, ...]]: From 24f1d9a87016134a895bdb2d455ad9e0032be62b Mon Sep 17 00:00:00 2001 From: Matthew Scroggs Date: Mon, 10 Jun 2024 12:08:54 +0100 Subject: [PATCH 08/15] remove isort --- .github/workflows/style-checks.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/style-checks.yml b/.github/workflows/style-checks.yml index 4ef1188f..11eecde5 100644 --- a/.github/workflows/style-checks.yml +++ b/.github/workflows/style-checks.yml @@ -22,8 +22,6 @@ jobs: name: Run ruff checks - run: python3 -m mypy . name: Run mypy checks - - run: python3 -m isort --check . - name: Run isort checks - run: | cd docs make html SPHINXOPTS="-W" From e6e11c8cc404f679e23605ec35a6ee920fceec54 Mon Sep 17 00:00:00 2001 From: Matthew Scroggs Date: Mon, 10 Jun 2024 12:11:57 +0100 Subject: [PATCH 09/15] add __all__ --- symfem/mappings.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/symfem/mappings.py b/symfem/mappings.py index 0582dc58..3be9c391 100644 --- a/symfem/mappings.py +++ b/symfem/mappings.py @@ -10,6 +10,27 @@ from symfem.geometry import PointType from symfem.symbols import x +__all__ = [ + "MappingNotImplemented", + "identity", + "l2", + "covariant", + "contravariant", + "double_covariant", + "double_contravariant", + "identity_inverse_transpose", + "l2_inverse_transpose", + "covariant_inverse_transpose", + "contravariant_inverse_transpose", + "identity_inverse", + "l2_inverse", + "covariant_inverse", + "contravariant_inverse", + "double_covariant_inverse", + "double_contravariant_inverse", + "get_mapping", +] + class MappingNotImplemented(NotImplementedError): """Exception thrown when a mapping is not implemented for an element.""" From 17a9a18c6804715c684329d6899bb8f8bb5ba6f6 Mon Sep 17 00:00:00 2001 From: Matthew Scroggs Date: Mon, 10 Jun 2024 12:29:12 +0100 Subject: [PATCH 10/15] __all__ --- symfem/elements/abf.py | 2 ++ symfem/elements/ac.py | 2 ++ symfem/elements/alfeld_sorokina.py | 2 ++ symfem/elements/argyris.py | 2 ++ symfem/elements/aw.py | 2 ++ symfem/elements/bddm.py | 2 ++ symfem/elements/bdfm.py | 2 ++ symfem/elements/bdm.py | 2 ++ symfem/elements/bell.py | 2 ++ symfem/elements/bernardi_raugel.py | 2 ++ symfem/elements/bernstein.py | 2 ++ symfem/elements/bfs.py | 2 ++ symfem/elements/bubble.py | 2 ++ symfem/elements/conforming_crouzeix_raviart.py | 2 ++ symfem/elements/crouzeix_raviart.py | 2 ++ symfem/elements/direct_serendipity.py | 2 ++ symfem/elements/dpc.py | 2 ++ symfem/elements/dual.py | 2 ++ symfem/elements/enriched_galerkin.py | 2 ++ symfem/elements/fortin_soulie.py | 2 ++ symfem/elements/guzman_neilan.py | 2 ++ symfem/elements/hct.py | 2 ++ symfem/elements/hermite.py | 2 ++ symfem/elements/hhj.py | 2 ++ symfem/elements/huang_zhang.py | 2 ++ symfem/elements/kmv.py | 2 ++ symfem/elements/lagrange.py | 2 ++ symfem/elements/lagrange_prism.py | 2 ++ symfem/elements/lagrange_pyramid.py | 2 ++ symfem/elements/morley.py | 2 ++ symfem/elements/morley_wang_xu.py | 2 ++ symfem/elements/mtw.py | 2 ++ symfem/elements/nedelec.py | 2 ++ symfem/elements/nedelec_prism.py | 2 ++ symfem/elements/p1_iso_p2.py | 2 ++ symfem/elements/p1_macro.py | 2 ++ symfem/elements/q.py | 2 ++ symfem/elements/rannacher_turek.py | 2 ++ symfem/elements/regge.py | 2 ++ symfem/elements/rhct.py | 2 ++ symfem/elements/rt.py | 2 ++ symfem/elements/serendipity.py | 2 ++ symfem/elements/taylor.py | 2 ++ symfem/elements/tnt.py | 2 ++ symfem/elements/transition.py | 2 ++ symfem/elements/trimmed_serendipity.py | 2 ++ symfem/elements/vector_enriched_galerkin.py | 2 ++ symfem/elements/wu_xu.py | 2 ++ 48 files changed, 96 insertions(+) diff --git a/symfem/elements/abf.py b/symfem/elements/abf.py index 4adff572..c41fe459 100644 --- a/symfem/elements/abf.py +++ b/symfem/elements/abf.py @@ -20,6 +20,8 @@ from symfem.elements.lagrange import Lagrange from symfem.elements.q import Nedelec +__all__ = ["ArnoldBoffiFalk"] + class ArnoldBoffiFalk(CiarletElement): """An Arnold-Boffi-Falk element.""" diff --git a/symfem/elements/ac.py b/symfem/elements/ac.py index 9af50b27..a2dffb8a 100644 --- a/symfem/elements/ac.py +++ b/symfem/elements/ac.py @@ -15,6 +15,8 @@ from symfem.symbols import x from symfem.elements.dpc import DPC +__all__ = ["AC"] + class AC(CiarletElement): """Arbogast-Correa Hdiv finite element.""" diff --git a/symfem/elements/alfeld_sorokina.py b/symfem/elements/alfeld_sorokina.py index 47460225..c0c41f27 100644 --- a/symfem/elements/alfeld_sorokina.py +++ b/symfem/elements/alfeld_sorokina.py @@ -15,6 +15,8 @@ from symfem.references import NonDefaultReferenceError, Reference from symfem.symbols import x +__all__ = ["AlfeldSorokina"] + class AlfeldSorokina(CiarletElement): """Alfeld-Sorokina finite element.""" diff --git a/symfem/elements/argyris.py b/symfem/elements/argyris.py index 40941bfd..11c85cc5 100644 --- a/symfem/elements/argyris.py +++ b/symfem/elements/argyris.py @@ -18,6 +18,8 @@ from symfem.polynomials import polynomial_set_1d from symfem.references import NonDefaultReferenceError, Reference +__all__ = ["Argyris"] + class Argyris(CiarletElement): """Argyris finite element.""" diff --git a/symfem/elements/aw.py b/symfem/elements/aw.py index da057fd8..c45a7430 100644 --- a/symfem/elements/aw.py +++ b/symfem/elements/aw.py @@ -22,6 +22,8 @@ from symfem.symbols import x from symfem.elements.lagrange import Lagrange +__all__ = ["ArnoldWinther", "NonConformingArnoldWinther"] + class ArnoldWinther(CiarletElement): """An Arnold-Winther element.""" diff --git a/symfem/elements/bddm.py b/symfem/elements/bddm.py index 11b1718a..442f0ca9 100644 --- a/symfem/elements/bddm.py +++ b/symfem/elements/bddm.py @@ -15,6 +15,8 @@ from symfem.symbols import x from symfem.elements.dpc import DPC, VectorDPC +__all__ = ["bddf_polyset", "BDDF"] + def bddf_polyset(reference: Reference, order: int) -> typing.List[FunctionInput]: """Create the polynomial basis for a BDDF element. diff --git a/symfem/elements/bdfm.py b/symfem/elements/bdfm.py index bf9430c1..80948319 100644 --- a/symfem/elements/bdfm.py +++ b/symfem/elements/bdfm.py @@ -16,6 +16,8 @@ from symfem.elements.dpc import DPC, VectorDPC from symfem.elements.lagrange import Lagrange, VectorLagrange +__all__ = ["bdfm_polyset", "BDFM"] + def bdfm_polyset(reference: Reference, order: int) -> typing.List[FunctionInput]: """Create the polynomial basis for a BDFM element. diff --git a/symfem/elements/bdm.py b/symfem/elements/bdm.py index d4f00d16..9ff73d66 100644 --- a/symfem/elements/bdm.py +++ b/symfem/elements/bdm.py @@ -15,6 +15,8 @@ from symfem.elements.lagrange import Lagrange from symfem.elements.nedelec import NedelecFirstKind +__all__ = ["BDM"] + class BDM(CiarletElement): """Brezzi-Douglas-Marini Hdiv finite element.""" diff --git a/symfem/elements/bell.py b/symfem/elements/bell.py index db269feb..3cf7a3ac 100644 --- a/symfem/elements/bell.py +++ b/symfem/elements/bell.py @@ -12,6 +12,8 @@ from symfem.references import Reference from symfem.symbols import x +__all__ = ["Bell"] + class Bell(CiarletElement): """Bell finite element.""" diff --git a/symfem/elements/bernardi_raugel.py b/symfem/elements/bernardi_raugel.py index 40eaccc2..54d9c2f8 100644 --- a/symfem/elements/bernardi_raugel.py +++ b/symfem/elements/bernardi_raugel.py @@ -22,6 +22,8 @@ from symfem.symbols import x from symfem.elements.lagrange import Lagrange +__all__ = ["BernardiRaugel"] + class BernardiRaugel(CiarletElement): """Bernardi-Raugel Hdiv finite element.""" diff --git a/symfem/elements/bernstein.py b/symfem/elements/bernstein.py index 175a6be4..adf25d1b 100644 --- a/symfem/elements/bernstein.py +++ b/symfem/elements/bernstein.py @@ -17,6 +17,8 @@ from symfem.references import Reference from symfem.symbols import AxisVariablesNotSingle, t, x +__all__ = ["single_choose", "choose", "bernstein_polynomials", "BernsteinFunctional", "Bernstein"] + def single_choose(n: int, k: int) -> sympy.core.expr.Expr: """Calculate choose function of a set of powers. diff --git a/symfem/elements/bfs.py b/symfem/elements/bfs.py index 3ce01f39..43634c78 100644 --- a/symfem/elements/bfs.py +++ b/symfem/elements/bfs.py @@ -12,6 +12,8 @@ from symfem.polynomials import quolynomial_set_1d from symfem.references import Reference +__all__ = ["BognerFoxSchmit"] + class BognerFoxSchmit(CiarletElement): """Bogner-Fox-Schmit finite element.""" diff --git a/symfem/elements/bubble.py b/symfem/elements/bubble.py index aa7247da..4fb88955 100644 --- a/symfem/elements/bubble.py +++ b/symfem/elements/bubble.py @@ -15,6 +15,8 @@ from symfem.references import Reference from symfem.elements.lagrange import Lagrange +__all__ = ["Bubble", "BubbleEnrichedLagrange", "BubbleEnrichedVectorLagrange"] + class Bubble(CiarletElement): """Bubble finite element.""" diff --git a/symfem/elements/conforming_crouzeix_raviart.py b/symfem/elements/conforming_crouzeix_raviart.py index 20bd19c5..d8940bd4 100644 --- a/symfem/elements/conforming_crouzeix_raviart.py +++ b/symfem/elements/conforming_crouzeix_raviart.py @@ -15,6 +15,8 @@ from symfem.references import NonDefaultReferenceError, Reference from symfem.symbols import x +__all__ = ["ConformingCrouzeixRaviart"] + class ConformingCrouzeixRaviart(CiarletElement): """Conforming Crouzeix-Raviart finite element.""" diff --git a/symfem/elements/crouzeix_raviart.py b/symfem/elements/crouzeix_raviart.py index 994474a4..3bd55dce 100644 --- a/symfem/elements/crouzeix_raviart.py +++ b/symfem/elements/crouzeix_raviart.py @@ -14,6 +14,8 @@ from symfem.quadrature import get_quadrature from symfem.references import Reference +__all__ = ["CrouzeixRaviart"] + class CrouzeixRaviart(CiarletElement): """Crouzeix-Raviart finite element.""" diff --git a/symfem/elements/direct_serendipity.py b/symfem/elements/direct_serendipity.py index 7e267e74..8f08fad4 100644 --- a/symfem/elements/direct_serendipity.py +++ b/symfem/elements/direct_serendipity.py @@ -9,6 +9,8 @@ from symfem.symbols import x from symfem.elements.dpc import DPC +__all__ = ["DirectSerendipity"] + class DirectSerendipity(DirectElement): """A direct serendipity element.""" diff --git a/symfem/elements/dpc.py b/symfem/elements/dpc.py index 6385647b..29b8d39a 100644 --- a/symfem/elements/dpc.py +++ b/symfem/elements/dpc.py @@ -17,6 +17,8 @@ from symfem.references import NonDefaultReferenceError, Reference from symfem.elements.lagrange import Lagrange +__all__ = ["DP", "VectorDPC"] + class DPC(CiarletElement): """A dPc element.""" diff --git a/symfem/elements/dual.py b/symfem/elements/dual.py index 4bfc1521..1fb19a9a 100644 --- a/symfem/elements/dual.py +++ b/symfem/elements/dual.py @@ -14,6 +14,8 @@ from symfem.piecewise_functions import PiecewiseFunction from symfem.references import DualPolygon, NonDefaultReferenceError +__all__ = ["DualCiarletElement", "Dual", "BuffaChristiansen", "RotatedBuffaChristiansen"] + class DualCiarletElement(FiniteElement): """Abstract barycentric finite element.""" diff --git a/symfem/elements/enriched_galerkin.py b/symfem/elements/enriched_galerkin.py index 76bf2ec1..a56043cc 100644 --- a/symfem/elements/enriched_galerkin.py +++ b/symfem/elements/enriched_galerkin.py @@ -9,6 +9,8 @@ from symfem.elements.lagrange import Lagrange from symfem.elements.q import Q +__all__ = ["EnrichedGalerkin"] + class EnrichedGalerkin(EnrichedElement): """An enriched Galerkin element.""" diff --git a/symfem/elements/fortin_soulie.py b/symfem/elements/fortin_soulie.py index f2da2b03..cafd6b78 100644 --- a/symfem/elements/fortin_soulie.py +++ b/symfem/elements/fortin_soulie.py @@ -14,6 +14,8 @@ from symfem.polynomials import polynomial_set_1d from symfem.references import NonDefaultReferenceError, Reference +__all__ = ["FortinSoulie"] + class FortinSoulie(CiarletElement): """Fortin-Soulie finite element.""" diff --git a/symfem/elements/guzman_neilan.py b/symfem/elements/guzman_neilan.py index 4bc710a5..18ed8218 100644 --- a/symfem/elements/guzman_neilan.py +++ b/symfem/elements/guzman_neilan.py @@ -18,6 +18,8 @@ from symfem.elements.bernardi_raugel import BernardiRaugel from symfem.elements.lagrange import Lagrange, VectorLagrange +__all__ = ["GuzmanNeilan", "make_piecewise_lagrange"] + class GuzmanNeilan(CiarletElement): """Guzman-Neilan Hdiv finite element.""" diff --git a/symfem/elements/hct.py b/symfem/elements/hct.py index eb37d850..46ad5f7e 100644 --- a/symfem/elements/hct.py +++ b/symfem/elements/hct.py @@ -20,6 +20,8 @@ from symfem.references import NonDefaultReferenceError, Reference from symfem.symbols import x +__all__ = ["HsiehCloughTocher"] + class HsiehCloughTocher(CiarletElement): """Hsieh-Clough-Tocher finite element.""" diff --git a/symfem/elements/hermite.py b/symfem/elements/hermite.py index d6cda30a..9499566a 100644 --- a/symfem/elements/hermite.py +++ b/symfem/elements/hermite.py @@ -12,6 +12,8 @@ from symfem.polynomials import polynomial_set_1d from symfem.references import Reference +__all__ = ["Hermite"] + class Hermite(CiarletElement): """Hermite finite element.""" diff --git a/symfem/elements/hhj.py b/symfem/elements/hhj.py index 1e9ea5f4..2d41e5d9 100644 --- a/symfem/elements/hhj.py +++ b/symfem/elements/hhj.py @@ -22,6 +22,8 @@ from symfem.references import NonDefaultReferenceError, Reference from symfem.elements.lagrange import Lagrange +__all__ = ["HellanHerrmannJohnson"] + class HellanHerrmannJohnson(CiarletElement): """A Hellan-Herrmann-Johnson element.""" diff --git a/symfem/elements/huang_zhang.py b/symfem/elements/huang_zhang.py index 455c2241..ac89306a 100644 --- a/symfem/elements/huang_zhang.py +++ b/symfem/elements/huang_zhang.py @@ -19,6 +19,8 @@ from symfem.symbols import x from symfem.elements.lagrange import Lagrange +__all__ = ["HuangZhang"] + class HuangZhang(CiarletElement): """Huang-Zhang finite element.""" diff --git a/symfem/elements/kmv.py b/symfem/elements/kmv.py index 51d8f961..9f8949ab 100644 --- a/symfem/elements/kmv.py +++ b/symfem/elements/kmv.py @@ -15,6 +15,8 @@ from symfem.references import NonDefaultReferenceError, Reference from symfem.symbols import x +__all__ = ["kmv_tri_polyset", "kmv_tet_polyset", "KongMulderVeldhuizen"] + def kmv_tri_polyset(m: int, mf: int) -> typing.List[FunctionInput]: """Create the polynomial set for a KMV space on a triangle. diff --git a/symfem/elements/lagrange.py b/symfem/elements/lagrange.py index c755aeb3..2380b420 100644 --- a/symfem/elements/lagrange.py +++ b/symfem/elements/lagrange.py @@ -22,6 +22,8 @@ from symfem.quadrature import get_quadrature from symfem.references import Reference +__all__ = ["Lagrange", "VectorLagrange", "MatrixLagrange", "SymmetricMatrixLagrange"] + class Lagrange(CiarletElement): """Lagrange finite element.""" diff --git a/symfem/elements/lagrange_prism.py b/symfem/elements/lagrange_prism.py index 24338c54..613fabeb 100644 --- a/symfem/elements/lagrange_prism.py +++ b/symfem/elements/lagrange_prism.py @@ -21,6 +21,8 @@ from symfem.quadrature import get_quadrature from symfem.references import NonDefaultReferenceError, Reference +__all__ = ["Lagrange", "VectorLagrange"] + class Lagrange(CiarletElement): """Lagrange finite element.""" diff --git a/symfem/elements/lagrange_pyramid.py b/symfem/elements/lagrange_pyramid.py index d5ab8c24..261cea6d 100644 --- a/symfem/elements/lagrange_pyramid.py +++ b/symfem/elements/lagrange_pyramid.py @@ -16,6 +16,8 @@ from symfem.quadrature import get_quadrature from symfem.references import NonDefaultReferenceError, Reference +__all__ = ["Lagrange"] + class Lagrange(CiarletElement): """Lagrange finite element.""" diff --git a/symfem/elements/morley.py b/symfem/elements/morley.py index 09515d56..638022d1 100644 --- a/symfem/elements/morley.py +++ b/symfem/elements/morley.py @@ -12,6 +12,8 @@ from symfem.polynomials import polynomial_set_1d from symfem.references import NonDefaultReferenceError, Reference +__all__ = ["Morley"] + class Morley(CiarletElement): """Morley finite element.""" diff --git a/symfem/elements/morley_wang_xu.py b/symfem/elements/morley_wang_xu.py index 521f2b46..816bab63 100644 --- a/symfem/elements/morley_wang_xu.py +++ b/symfem/elements/morley_wang_xu.py @@ -17,6 +17,8 @@ from symfem.polynomials import polynomial_set_1d from symfem.references import NonDefaultReferenceError, Reference +__all__ = ["MorleyWangXu"] + class MorleyWangXu(CiarletElement): """Morley-Wang-Xu finite element.""" diff --git a/symfem/elements/mtw.py b/symfem/elements/mtw.py index 1445235b..7619ace3 100644 --- a/symfem/elements/mtw.py +++ b/symfem/elements/mtw.py @@ -22,6 +22,8 @@ from symfem.elements.lagrange import Lagrange from symfem.elements.nedelec import NedelecFirstKind +__all__ = ["MardalTaiWinther"] + class MardalTaiWinther(CiarletElement): """Mardal-Tai-Winther Hdiv finite element.""" diff --git a/symfem/elements/nedelec.py b/symfem/elements/nedelec.py index bc5187f4..306ab378 100644 --- a/symfem/elements/nedelec.py +++ b/symfem/elements/nedelec.py @@ -15,6 +15,8 @@ from symfem.elements.lagrange import Lagrange, VectorLagrange from symfem.elements.rt import RaviartThomas +__all__ = ["NedelecFirstKind", "NedelecSecondKind"] + class NedelecFirstKind(CiarletElement): """Nedelec first kind Hcurl finite element.""" diff --git a/symfem/elements/nedelec_prism.py b/symfem/elements/nedelec_prism.py index 22f10022..3eead6fa 100644 --- a/symfem/elements/nedelec_prism.py +++ b/symfem/elements/nedelec_prism.py @@ -17,6 +17,8 @@ from symfem.elements.lagrange import Lagrange, VectorLagrange from symfem.elements.q import RaviartThomas as QRT +__all__ = ["Nedelec"] + class Nedelec(CiarletElement): """Nedelec Hcurl finite element.""" diff --git a/symfem/elements/p1_iso_p2.py b/symfem/elements/p1_iso_p2.py index ee43a66e..e81d432a 100644 --- a/symfem/elements/p1_iso_p2.py +++ b/symfem/elements/p1_iso_p2.py @@ -15,6 +15,8 @@ from symfem.piecewise_functions import PiecewiseFunction from symfem.references import Reference +__all__ = ["P1IsoP2Interval", "P1IsoP2Tri", "P1IsoP2Quad"] + class P1IsoP2Interval(CiarletElement): """P1-iso-P2 finite element on an interval.""" diff --git a/symfem/elements/p1_macro.py b/symfem/elements/p1_macro.py index 4999a1a5..8bef9b0b 100644 --- a/symfem/elements/p1_macro.py +++ b/symfem/elements/p1_macro.py @@ -16,6 +16,8 @@ from symfem.references import Reference from symfem.symbols import x +__all__ = ["P1Macro"] + class P1Macro(CiarletElement): """P1 macro finite element on a triangle.""" diff --git a/symfem/elements/q.py b/symfem/elements/q.py index 32f6d0f6..b3d09235 100644 --- a/symfem/elements/q.py +++ b/symfem/elements/q.py @@ -28,6 +28,8 @@ from symfem.quadrature import get_quadrature from symfem.references import NonDefaultReferenceError, Reference +__all__ = ["Q", "VectorQ", "Nedelec", "RaviartThomas"] + class Q(CiarletElement): """A Q element.""" diff --git a/symfem/elements/rannacher_turek.py b/symfem/elements/rannacher_turek.py index 697bfcfc..a25f5dfc 100644 --- a/symfem/elements/rannacher_turek.py +++ b/symfem/elements/rannacher_turek.py @@ -12,6 +12,8 @@ from symfem.references import NonDefaultReferenceError, Reference from symfem.symbols import x +__all__ = ["RannacherTurek"] + class RannacherTurek(CiarletElement): """Rannacher-Turek finite element.""" diff --git a/symfem/elements/regge.py b/symfem/elements/regge.py index 69cc3bf6..12ea4629 100644 --- a/symfem/elements/regge.py +++ b/symfem/elements/regge.py @@ -27,6 +27,8 @@ from symfem.symbols import t, x from symfem.elements.lagrange import Lagrange +__all__ = ["Regge", "ReggeTP"] + class Regge(CiarletElement): """A Regge element on a simplex.""" diff --git a/symfem/elements/rhct.py b/symfem/elements/rhct.py index 2deb094b..df2fa646 100644 --- a/symfem/elements/rhct.py +++ b/symfem/elements/rhct.py @@ -15,6 +15,8 @@ from symfem.references import NonDefaultReferenceError, Reference from symfem.symbols import x +__all__ = ["ReducedHsiehCloughTocher"] + class ReducedHsiehCloughTocher(CiarletElement): """Reduced Hsieh-Clough-Tocher finite element.""" diff --git a/symfem/elements/rt.py b/symfem/elements/rt.py index 9c93ede1..369075bc 100644 --- a/symfem/elements/rt.py +++ b/symfem/elements/rt.py @@ -14,6 +14,8 @@ from symfem.references import Reference from symfem.elements.lagrange import Lagrange, VectorLagrange +__all__ = ["RaviartThomas"] + class RaviartThomas(CiarletElement): """Raviart-Thomas Hdiv finite element.""" diff --git a/symfem/elements/serendipity.py b/symfem/elements/serendipity.py index 54580e45..94718ec2 100644 --- a/symfem/elements/serendipity.py +++ b/symfem/elements/serendipity.py @@ -26,6 +26,8 @@ from symfem.references import NonDefaultReferenceError, Reference from symfem.elements.dpc import DPC, VectorDPC +__all__ = ["Serendipity", "SerendipityCurl", "SerendipityDiv"] + class Serendipity(CiarletElement): """A serendipity element.""" diff --git a/symfem/elements/taylor.py b/symfem/elements/taylor.py index e7568c80..f23402c9 100644 --- a/symfem/elements/taylor.py +++ b/symfem/elements/taylor.py @@ -11,6 +11,8 @@ from symfem.references import Reference from symfem.elements.lagrange import Lagrange +__all__ = ["Taylor"] + class Taylor(CiarletElement): """Taylor finite element.""" diff --git a/symfem/elements/tnt.py b/symfem/elements/tnt.py index 6837b2f6..6438d81a 100644 --- a/symfem/elements/tnt.py +++ b/symfem/elements/tnt.py @@ -25,6 +25,8 @@ from symfem.symbols import t, x from symfem.elements.q import Q +__all__ = ["p", "b", "TNT", "TNTcurl", "TNTdiv"] + def p(k: int, v: sympy.core.symbol.Symbol) -> ScalarFunction: """Return the kth Legendre polynomial. diff --git a/symfem/elements/transition.py b/symfem/elements/transition.py index dbf57ba6..1303e5d9 100644 --- a/symfem/elements/transition.py +++ b/symfem/elements/transition.py @@ -14,6 +14,8 @@ from symfem.symbols import x from symfem.elements.lagrange import Lagrange +__all__ = ["Transistion"] + class Transition(CiarletElement): """Transition finite element.""" diff --git a/symfem/elements/trimmed_serendipity.py b/symfem/elements/trimmed_serendipity.py index 6d682c34..2fa90ff5 100644 --- a/symfem/elements/trimmed_serendipity.py +++ b/symfem/elements/trimmed_serendipity.py @@ -22,6 +22,8 @@ from symfem.symbols import t, x from symfem.elements.dpc import DPC, VectorDPC +__all__ = ["TrimmedSerendipityHcurl", "TrimmedSerendipityHdiv"] + class TrimmedSerendipityHcurl(CiarletElement): """Trimmed serendipity Hcurl finite element.""" diff --git a/symfem/elements/vector_enriched_galerkin.py b/symfem/elements/vector_enriched_galerkin.py index 10a49ea7..1272fed0 100644 --- a/symfem/elements/vector_enriched_galerkin.py +++ b/symfem/elements/vector_enriched_galerkin.py @@ -14,6 +14,8 @@ from symfem.elements.lagrange import VectorLagrange from symfem.elements.q import VectorQ +__all__ = ["Enrichment", "VectorEnrichedGalerkin"] + class Enrichment(CiarletElement): """An LF enriched Galerkin element.""" diff --git a/symfem/elements/wu_xu.py b/symfem/elements/wu_xu.py index 38758421..f9d3e0e8 100644 --- a/symfem/elements/wu_xu.py +++ b/symfem/elements/wu_xu.py @@ -17,6 +17,8 @@ from symfem.polynomials import polynomial_set_1d from symfem.references import NonDefaultReferenceError, Reference +__all__ = ["derivatives", "WuXu"] + def derivatives(dim: int, order: int) -> typing.List[typing.Tuple[int, ...]]: """Return all the orders of a multidimensional derivative. From 44ca9933e2f7813f4e99acbaf57a0f0939debca6 Mon Sep 17 00:00:00 2001 From: Matthew Scroggs Date: Mon, 10 Jun 2024 12:30:10 +0100 Subject: [PATCH 11/15] typos --- symfem/elements/dpc.py | 2 +- symfem/elements/transition.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/symfem/elements/dpc.py b/symfem/elements/dpc.py index 29b8d39a..c895a7c0 100644 --- a/symfem/elements/dpc.py +++ b/symfem/elements/dpc.py @@ -17,7 +17,7 @@ from symfem.references import NonDefaultReferenceError, Reference from symfem.elements.lagrange import Lagrange -__all__ = ["DP", "VectorDPC"] +__all__ = ["DPC", "VectorDPC"] class DPC(CiarletElement): diff --git a/symfem/elements/transition.py b/symfem/elements/transition.py index 1303e5d9..b216c8ab 100644 --- a/symfem/elements/transition.py +++ b/symfem/elements/transition.py @@ -14,7 +14,7 @@ from symfem.symbols import x from symfem.elements.lagrange import Lagrange -__all__ = ["Transistion"] +__all__ = ["Transition"] class Transition(CiarletElement): From 2c420ac01b2bb191bd3991fd638064cb906e0914 Mon Sep 17 00:00:00 2001 From: Matthew Scroggs Date: Mon, 10 Jun 2024 12:49:12 +0100 Subject: [PATCH 12/15] __all__ --- symfem/basis_functions.py | 2 ++ symfem/caching.py | 2 ++ symfem/create.py | 2 ++ symfem/finite_element.py | 10 ++++++++++ symfem/functionals.py | 24 ++++++++++++++++++++++++ symfem/functions.py | 13 +++++++++++++ symfem/geometry.py | 13 +++++++++++++ symfem/moments.py | 2 ++ symfem/piecewise_functions.py | 2 ++ symfem/plotting.py | 16 ++++++++++++++++ symfem/quadrature.py | 2 ++ symfem/references.py | 16 ++++++++++++++++ symfem/symbols.py | 2 ++ symfem/utils.py | 2 ++ 14 files changed, 108 insertions(+) diff --git a/symfem/basis_functions.py b/symfem/basis_functions.py index 161ec054..42859fe7 100644 --- a/symfem/basis_functions.py +++ b/symfem/basis_functions.py @@ -20,6 +20,8 @@ from symfem.references import Reference from symfem.symbols import AxisVariables, AxisVariablesNotSingle, t, x +__all__ = ["BasisFunction", "SubbedBasisFunction"] + class BasisFunction(AnyFunction): """A basis function of a finite element. diff --git a/symfem/caching.py b/symfem/caching.py index 22005b62..53ff17a1 100644 --- a/symfem/caching.py +++ b/symfem/caching.py @@ -18,6 +18,8 @@ assert os.path.isdir(CACHE_DIR) +__all__ = ["load_cached_matrix", "save_cached_matrix", "matrix_to_string", "matrix_from_string"] + def load_cached_matrix( matrix_type: str, cache_id: str, size: typing.Tuple[int, int] diff --git a/symfem/create.py b/symfem/create.py index 293f5e67..e255de20 100644 --- a/symfem/create.py +++ b/symfem/create.py @@ -14,6 +14,8 @@ _elementmap: _typing.Dict[str, _typing.Dict[str, _typing.Type]] = {} _elementlist: _typing.List[_typing.Type] = [] +__all__ = ["add_element", "create_reference", "create_element"] + def add_element(element_class: _typing.Type): """Add an element to Symfem. diff --git a/symfem/finite_element.py b/symfem/finite_element.py index 12b7333e..68d38212 100644 --- a/symfem/finite_element.py +++ b/symfem/finite_element.py @@ -28,6 +28,16 @@ from symfem.utils import allequal from symfem.version import version +__all__ = [ + "TabulatedBasis", + "NoTensorProduct", + "FiniteElement", + "CiarletElement", + "DirectElement", + "EnrichedElement", + "ElementBasisFunction", +] + TabulatedBasis = typing.Union[ typing.List[typing.Union[sympy.core.expr.Expr, int]], typing.List[typing.Tuple[typing.Union[sympy.core.expr.Expr, int], ...]], diff --git a/symfem/functionals.py b/symfem/functionals.py index 8da05f2c..b4c20a2a 100644 --- a/symfem/functionals.py +++ b/symfem/functionals.py @@ -20,6 +20,30 @@ ScalarValueOrFloat = typing.Union[sympy.core.expr.Expr, float] +__all__ = [ + "BaseFunctional", + "PointEvaluation", + "WeightedPointEvaluation", + "DerivativePointEvaluation", + "PointDirectionalDerivativeEvaluation", + "PointNormalDerivativeEvaluation", + "PointComponentSecondDerivativeEvaluation", + "PointInnerProduct", + "DotPointEvaluation", + "PointDivergenceEvaluation", + "IntegralAgainst", + "IntegralOfDivergenceAgainst", + "IntegralOfDirectionalMultiderivative", + "IntegralMoment", + "DerivativeIntegralMoment", + "DivergenceIntegralMoment", + "TangentIntegralMoment", + "NormalIntegralMoment", + "NormalDerivativeIntegralMoment", + "InnerProductIntegralMoment", + "NormalInnerProductIntegralMoment", +] + def _to_tex(f: FunctionInput, tfrac: bool = False) -> str: r"""Convert an expresson to TeX. diff --git a/symfem/functions.py b/symfem/functions.py index 15f0eea7..121c941d 100644 --- a/symfem/functions.py +++ b/symfem/functions.py @@ -12,6 +12,19 @@ from symfem.geometry import PointType from symfem.symbols import AxisVariables, AxisVariablesNotSingle, t, x +__all__ = [ + "SingleSympyFormat", + "SympyFormat", + "ValuesToSubstitute", + "AnyFunction", + "ScalarFunction", + "VectorFunction", + "MatrixFunction", + "FunctionInput", + "parse_function_input", + "parse_function_list_input", +] + SingleSympyFormat = typing.Union[ sympy.core.expr.Expr, typing.Tuple[sympy.core.expr.Expr, ...], diff --git a/symfem/geometry.py b/symfem/geometry.py index fd4a64cf..8ad84bab 100644 --- a/symfem/geometry.py +++ b/symfem/geometry.py @@ -4,6 +4,19 @@ import sympy +__all__ = [ + "PointType", + "SetOfPoints", + "PointTypeInput", + "SetOfPointsInput", + "parse_set_of_points_input", + "parse_point_input", + "point_in_interval", + "point_in_triangle", + "point_in_quadrilateral", + "point_in_tetrahedron", +] + PointType = typing.Tuple[sympy.core.expr.Expr, ...] SetOfPoints = typing.Tuple[PointType, ...] PointTypeInput = typing.Union[ diff --git a/symfem/moments.py b/symfem/moments.py index b5e1ca9e..f58d1850 100644 --- a/symfem/moments.py +++ b/symfem/moments.py @@ -5,6 +5,8 @@ from symfem.functionals import BaseFunctional from symfem.references import Reference +__all__ = ["MomentType", "SingleMomentTypeInput", "MomentTypeInput", "make_integral_moment_dofs"] + MomentType = typing.Tuple[ typing.Type, typing.Type, int, typing.Union[str, None], typing.Dict[str, typing.Any] ] diff --git a/symfem/piecewise_functions.py b/symfem/piecewise_functions.py index 0e1daf1d..3d45b7c9 100644 --- a/symfem/piecewise_functions.py +++ b/symfem/piecewise_functions.py @@ -31,6 +31,8 @@ from symfem.references import Reference from symfem.symbols import AxisVariables, AxisVariablesNotSingle, t, x +__all__ = ["PiecewiseFunction"] + class PiecewiseFunction(AnyFunction): """A piecewise function.""" diff --git a/symfem/plotting.py b/symfem/plotting.py index 8edac690..0f6d6d2c 100644 --- a/symfem/plotting.py +++ b/symfem/plotting.py @@ -15,6 +15,22 @@ parse_set_of_points_input, ) +__all__ = [ + "PointOrFunction", + "SetOfPointsOrFunctions", + "tex_font_size", + "Colors", + "colors", + "PictureElement", + "Line", + "Bezier", + "Arrow", + "NCircle", + "Fill", + "Math", + "Picture", +] + PointOrFunction = typing.Union[PointTypeInput, AnyFunction] SetOfPointsOrFunctions = typing.Union[ typing.List[PointOrFunction], typing.Tuple[PointOrFunction, ...] diff --git a/symfem/quadrature.py b/symfem/quadrature.py index 08ca43ca..ed3b97f1 100644 --- a/symfem/quadrature.py +++ b/symfem/quadrature.py @@ -4,6 +4,8 @@ import sympy +__all__ = ["Scalar", "equispaced", "lobatto", "radau", "legendre", "get_quadrature"] + Scalar = typing.Union[sympy.core.expr.Expr, int] diff --git a/symfem/references.py b/symfem/references.py index 5f591df0..7716b5f2 100644 --- a/symfem/references.py +++ b/symfem/references.py @@ -19,6 +19,22 @@ ) from symfem.symbols import AxisVariablesNotSingle, t, x +__all__ = [ + "LatticeWithLines", + "IntLimits", + "NonDefaultReferenceError", + "Reference", + "Point", + "Interval", + "Triangle", + "Tetrahedron", + "Quadrilateral", + "Hexahedron", + "Prism", + "Pyramid", + "DualPolygon", +] + LatticeWithLines = typing.Tuple[SetOfPoints, typing.List[typing.Tuple[int, int]]] IntLimits = typing.List[ typing.Union[ diff --git a/symfem/symbols.py b/symfem/symbols.py index f2a54756..4525704b 100644 --- a/symfem/symbols.py +++ b/symfem/symbols.py @@ -4,6 +4,8 @@ import sympy +__all__ = ["x", "t", "AxisVariablesNotSingle", "AxisVariables"] + x = (sympy.Symbol("x"), sympy.Symbol("y"), sympy.Symbol("z")) t = (sympy.Symbol("t0"), sympy.Symbol("t1"), sympy.Symbol("t2")) diff --git a/symfem/utils.py b/symfem/utils.py index 58df9927..517784a5 100644 --- a/symfem/utils.py +++ b/symfem/utils.py @@ -6,6 +6,8 @@ from symfem.functions import ScalarFunction +__all__ = ["allequal"] + def allequal(a: typing.Any, b: typing.Any) -> bool: """Test if two items that may be nested lists/tuples are equal. From 29bb864acc63bd289a80c31952e7e9afe2d7b17a Mon Sep 17 00:00:00 2001 From: Matthew Scroggs Date: Mon, 10 Jun 2024 12:51:09 +0100 Subject: [PATCH 13/15] __all__ --- symfem/polynomials/dual.py | 2 ++ symfem/polynomials/legendre.py | 2 ++ symfem/polynomials/lobatto.py | 2 ++ symfem/polynomials/polysets.py | 2 ++ 4 files changed, 8 insertions(+) diff --git a/symfem/polynomials/dual.py b/symfem/polynomials/dual.py index aefbd084..a1a369e6 100644 --- a/symfem/polynomials/dual.py +++ b/symfem/polynomials/dual.py @@ -6,6 +6,8 @@ from symfem.functions import ScalarFunction +__all__ = [] + def l2_dual(cell: str, poly: typing.List[ScalarFunction]) -> typing.List[ScalarFunction]: """Compute the L2 dual of a set of polynomials. diff --git a/symfem/polynomials/legendre.py b/symfem/polynomials/legendre.py index 7c9637d4..c7878039 100644 --- a/symfem/polynomials/legendre.py +++ b/symfem/polynomials/legendre.py @@ -7,6 +7,8 @@ from symfem.functions import ScalarFunction from symfem.symbols import AxisVariablesNotSingle, x +__all__ = [] + def _jrc( a, n diff --git a/symfem/polynomials/lobatto.py b/symfem/polynomials/lobatto.py index 65c090d3..c2816245 100644 --- a/symfem/polynomials/lobatto.py +++ b/symfem/polynomials/lobatto.py @@ -7,6 +7,8 @@ from symfem.polynomials.dual import l2_dual from symfem.polynomials.legendre import orthonormal_basis +__all__ = [] + def lobatto_basis_interval(order: int) -> typing.List[ScalarFunction]: """Get Lobatto polynomials on an interval. diff --git a/symfem/polynomials/polysets.py b/symfem/polynomials/polysets.py index d35659c7..3d20e7e1 100644 --- a/symfem/polynomials/polysets.py +++ b/symfem/polynomials/polysets.py @@ -6,6 +6,8 @@ from symfem.functions import ScalarFunction, VectorFunction from symfem.symbols import AxisVariablesNotSingle, x +__all__ = [] + def polynomial_set_1d( dim: int, order: int, variables: AxisVariablesNotSingle = x From d4bd7d349b1c9dc4e03d23b9ddcf3ba20dd42658 Mon Sep 17 00:00:00 2001 From: Matthew Scroggs Date: Mon, 10 Jun 2024 12:53:05 +0100 Subject: [PATCH 14/15] typing --- symfem/polynomials/dual.py | 2 +- symfem/polynomials/legendre.py | 2 +- symfem/polynomials/lobatto.py | 2 +- symfem/polynomials/polysets.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/symfem/polynomials/dual.py b/symfem/polynomials/dual.py index a1a369e6..a4ba0e20 100644 --- a/symfem/polynomials/dual.py +++ b/symfem/polynomials/dual.py @@ -6,7 +6,7 @@ from symfem.functions import ScalarFunction -__all__ = [] +__all__: typing.List[str] = [] def l2_dual(cell: str, poly: typing.List[ScalarFunction]) -> typing.List[ScalarFunction]: diff --git a/symfem/polynomials/legendre.py b/symfem/polynomials/legendre.py index c7878039..a786642a 100644 --- a/symfem/polynomials/legendre.py +++ b/symfem/polynomials/legendre.py @@ -7,7 +7,7 @@ from symfem.functions import ScalarFunction from symfem.symbols import AxisVariablesNotSingle, x -__all__ = [] +__all__: typing.List[str] = [] def _jrc( diff --git a/symfem/polynomials/lobatto.py b/symfem/polynomials/lobatto.py index c2816245..adc8dbd1 100644 --- a/symfem/polynomials/lobatto.py +++ b/symfem/polynomials/lobatto.py @@ -7,7 +7,7 @@ from symfem.polynomials.dual import l2_dual from symfem.polynomials.legendre import orthonormal_basis -__all__ = [] +__all__: typing.List[str] = [] def lobatto_basis_interval(order: int) -> typing.List[ScalarFunction]: diff --git a/symfem/polynomials/polysets.py b/symfem/polynomials/polysets.py index 3d20e7e1..c4133a4e 100644 --- a/symfem/polynomials/polysets.py +++ b/symfem/polynomials/polysets.py @@ -6,7 +6,7 @@ from symfem.functions import ScalarFunction, VectorFunction from symfem.symbols import AxisVariablesNotSingle, x -__all__ = [] +__all__: typing.List[str] = [] def polynomial_set_1d( From d7a4d7a035fc6ca55ac9e41c5ad6556d0d62daa3 Mon Sep 17 00:00:00 2001 From: Matthew Scroggs Date: Mon, 10 Jun 2024 12:54:30 +0100 Subject: [PATCH 15/15] undo changes to workflow --- .github/workflows/run-tests.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index e6d6a829..903252b5 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -3,8 +3,7 @@ name: 🧪 Tests on: push: branches: -# - "**" - - main + - "**" pull_request: branches: - main