Skip to content

Commit f340caa

Browse files
authored
Add P1-iso-P2 on interval (#232)
* Add P1 iso P2 on interval * changelog * is not == * piecewisefunction * mypy * 1d piecewise functions * flake * isort
1 parent 0cb3808 commit f340caa

File tree

8 files changed

+79
-11
lines changed

8 files changed

+79
-11
lines changed

CHANGELOG_SINCE_LAST_VERSION.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66
- Allow element creation on non-default references
77
- Corrected Bell element
88
- Corrected legendre and lobatto variants of Lagrange
9+
- Add P1-iso-P2 elemen on interval

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ The reference interval has vertices (0,) and (1,). Its sub-entities are numbered
148148
- Hermite
149149
- Lagrange (alternative names: P)
150150
- Morley-Wang-Xu (alternative names: MWX)
151+
- P1-iso-P2 (alternative names: P2-iso-P1, iso-P2 P1)
151152
- serendipity (alternative names: S)
152153
- Taylor (alternative names: discontinuous Taylor)
153154
- vector Lagrange (alternative names: vP)

symfem/elements/p1_iso_p2.py

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,45 @@
1414
from ..geometry import SetOfPoints
1515
from ..piecewise_functions import PiecewiseFunction
1616
from ..references import Reference
17-
from ..symbols import x as x_variables
17+
18+
19+
class P1IsoP2Interval(CiarletElement):
20+
"""P1-iso-P2 finite element on an interval."""
21+
22+
def __init__(self, reference: Reference, order: int):
23+
"""Create the element.
24+
25+
Args:
26+
reference: The reference element
27+
order: The polynomial order
28+
"""
29+
zero = reference.get_point((sympy.Integer(0), ))
30+
half = reference.get_point((sympy.Rational(1, 2), ))
31+
one = reference.get_point((sympy.Integer(1), ))
32+
33+
x = reference.get_inverse_map_to_self()[0]
34+
poly: typing.List[FunctionInput] = [
35+
PiecewiseFunction({(zero, half): 1 - 2 * x, (half, one): 0}, 1),
36+
PiecewiseFunction({(zero, half): 2 * x, (half, one): 2 - 2 * x}, 1),
37+
PiecewiseFunction({(zero, half): 0, (half, one): 2 * x - 1}, 1),
38+
]
39+
40+
dofs: ListOfFunctionals = []
41+
for v_n, v in enumerate(reference.vertices):
42+
dofs.append(PointEvaluation(reference, v, entity=(0, v_n)))
43+
entity = reference.sub_entity(1, 0)
44+
dofs.append(PointEvaluation(reference, entity.midpoint(), entity=(1, 0)))
45+
46+
super().__init__(
47+
reference, order, poly, dofs, reference.tdim, 1
48+
)
49+
50+
names = ["P1-iso-P2", "P2-iso-P1", "iso-P2 P1"]
51+
references = ["interval"]
52+
min_order = 1
53+
max_order = 1
54+
continuity = "C0"
55+
last_updated = "2023.08"
1856

1957

2058
class P1IsoP2Tri(CiarletElement):
@@ -37,10 +75,10 @@ def __init__(self, reference: Reference, order: int):
3775
((zero, half), (half, half), (half, zero)),
3876
]
3977
poly: typing.List[FunctionInput] = []
40-
x = x_variables[0]
41-
y = x_variables[1]
42-
c = 1 - x - y
4378
invmap = reference.get_inverse_map_to_self()
79+
x = invmap[0]
80+
y = invmap[1]
81+
c = 1 - x - y
4482
for pieces in [
4583
{0: 2 * c - 1},
4684
{1: 2 * x - 1},
@@ -52,7 +90,7 @@ def __init__(self, reference: Reference, order: int):
5290
poly.append(PiecewiseFunction({
5391
tuple(
5492
reference.get_point(pt) for pt in q
55-
): pieces[i].subs(x, invmap[0]).subs(y, invmap[1]) if i in pieces else 0
93+
): pieces[i] if i in pieces else 0
5694
for i, q in enumerate(tris)
5795
}, 2))
5896

@@ -95,9 +133,9 @@ def __init__(self, reference: Reference, order: int):
95133
((half, half), (one, half), (half, one), (one, one)),
96134
]
97135
poly: typing.List[FunctionInput] = []
98-
x = x_variables[0]
99-
y = x_variables[1]
100136
invmap = reference.get_inverse_map_to_self()
137+
x = invmap[0]
138+
y = invmap[1]
101139
for pieces in [
102140
{0: (1 - 2 * x) * (1 - 2 * y)},
103141
{1: (2 * x - 1) * (1 - 2 * y)},
@@ -112,7 +150,7 @@ def __init__(self, reference: Reference, order: int):
112150
poly.append(PiecewiseFunction({
113151
tuple(
114152
reference.get_point(pt) for pt in q
115-
): pieces[i].subs(x, invmap[0]).subs(y, invmap[1]) if i in pieces else 0
153+
): pieces[i] if i in pieces else 0
116154
for i, q in enumerate(quads)}, 2))
117155

118156
dofs: ListOfFunctionals = []

symfem/finite_element.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,10 @@ def get_piece(f, point):
357357
return tuple(get_piece(g, point) for g in f)
358358
return f
359359

360-
if self.reference.tdim == 2:
360+
if self.reference.tdim == 1:
361+
f = get_piece(f, (0, ))
362+
g = get_piece(g, (0, ))
363+
elif self.reference.tdim == 2:
361364
f = get_piece(f, (0, sympy.Rational(1, 2)))
362365
g = get_piece(g, (0, sympy.Rational(1, 2)))
363366
elif self.reference.tdim == 3:

symfem/geometry.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,25 @@ def _vdot(v: PointType, w: PointType) -> sympy.core.expr.Expr:
8787
return out
8888

8989

90+
def point_in_interval(point: PointType, interval: SetOfPoints) -> bool:
91+
"""Check if a point is inside an interval.
92+
93+
Args:
94+
point: The point
95+
interval: The vertices of the interval
96+
97+
Returns:
98+
Is the point inside the interval?
99+
"""
100+
v = _vsub(point, interval[0])[0] / _vsub(interval[1], interval[0])[0]
101+
102+
if isinstance(v, sympy.Float) and _is_close(v, 0):
103+
v = sympy.Integer(0)
104+
if isinstance(v, sympy.Float) and _is_close(v, 1):
105+
v = sympy.Integer(1)
106+
return 0 <= v <= 1
107+
108+
90109
def point_in_triangle(point: PointType, triangle: SetOfPoints) -> bool:
91110
"""Check if a point is inside a triangle.
92111

symfem/piecewise_functions.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
from .functions import (AnyFunction, FunctionInput, ScalarFunction, SympyFormat, ValuesToSubstitute,
1010
VectorFunction, _to_sympy_format, parse_function_input)
1111
from .geometry import (PointType, SetOfPoints, SetOfPointsInput, parse_set_of_points_input,
12-
point_in_quadrilateral, point_in_tetrahedron, point_in_triangle)
12+
point_in_interval, point_in_quadrilateral, point_in_tetrahedron,
13+
point_in_triangle)
1314
from .references import Reference
1415
from .symbols import AxisVariables, AxisVariablesNotSingle, t, x
1516

@@ -113,6 +114,10 @@ def get_piece(self, point: PointType) -> AnyFunction:
113114
Returns:
114115
The piece of the function that is valid at that point
115116
"""
117+
if self.tdim == 1:
118+
for cell, value in self._pieces.items():
119+
if point_in_interval(point[:1], cell):
120+
return value
116121
if self.tdim == 2:
117122
for cell, value in self._pieces.items():
118123
if len(cell) == 3:

symfem/references.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ def __eq__(self, other: object) -> bool:
220220
"""Check if two references are equal."""
221221
if not isinstance(other, Reference):
222222
return False
223-
return type(self) == type(other) and self.vertices == other.vertices
223+
return type(self) is type(other) and self.vertices == other.vertices
224224

225225
def __hash__(self) -> int:
226226
"""Check if two references are equal."""

test/utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"Wu-Xu": [({}, [2])],
2323
"MWX": [({}, [1])],
2424
"EG": [({}, range(1, 5))],
25+
"P1-iso-P2": [({}, [1])],
2526
},
2627
"triangle": {
2728
"P": [({"variant": "equispaced"}, range(5))],

0 commit comments

Comments
 (0)